OSDN Git Service

[TMP] JSON.stringify(execObj, null '\t'
[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         __webpack_require__(18);
62
63
64 /***/ },
65 /* 1 */
66 /***/ function(module, exports, __webpack_require__) {
67
68         __webpack_require__(2);
69         module.exports = angular;
70
71
72 /***/ },
73 /* 2 */
74 /***/ function(module, exports) {
75
76         /**
77          * @license AngularJS v1.4.8
78          * (c) 2010-2015 Google, Inc. http://angularjs.org
79          * License: MIT
80          */
81         (function(window, document, undefined) {'use strict';
82
83         /**
84          * @description
85          *
86          * This object provides a utility for producing rich Error messages within
87          * Angular. It can be called as follows:
88          *
89          * var exampleMinErr = minErr('example');
90          * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
91          *
92          * The above creates an instance of minErr in the example namespace. The
93          * resulting error will have a namespaced error code of example.one.  The
94          * resulting error will replace {0} with the value of foo, and {1} with the
95          * value of bar. The object is not restricted in the number of arguments it can
96          * take.
97          *
98          * If fewer arguments are specified than necessary for interpolation, the extra
99          * interpolation markers will be preserved in the final string.
100          *
101          * Since data will be parsed statically during a build step, some restrictions
102          * are applied with respect to how minErr instances are created and called.
103          * Instances should have names of the form namespaceMinErr for a minErr created
104          * using minErr('namespace') . Error codes, namespaces and template strings
105          * should all be static strings, not variables or general expressions.
106          *
107          * @param {string} module The namespace to use for the new minErr instance.
108          * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
109          *   error from returned function, for cases when a particular type of error is useful.
110          * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
111          */
112
113         function minErr(module, ErrorConstructor) {
114           ErrorConstructor = ErrorConstructor || Error;
115           return function() {
116             var SKIP_INDEXES = 2;
117
118             var templateArgs = arguments,
119               code = templateArgs[0],
120               message = '[' + (module ? module + ':' : '') + code + '] ',
121               template = templateArgs[1],
122               paramPrefix, i;
123
124             message += template.replace(/\{\d+\}/g, function(match) {
125               var index = +match.slice(1, -1),
126                 shiftedIndex = index + SKIP_INDEXES;
127
128               if (shiftedIndex < templateArgs.length) {
129                 return toDebugString(templateArgs[shiftedIndex]);
130               }
131
132               return match;
133             });
134
135             message += '\nhttp://errors.angularjs.org/1.4.8/' +
136               (module ? module + '/' : '') + code;
137
138             for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
139               message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
140                 encodeURIComponent(toDebugString(templateArgs[i]));
141             }
142
143             return new ErrorConstructor(message);
144           };
145         }
146
147         /* We need to tell jshint what variables are being exported */
148         /* global angular: true,
149           msie: true,
150           jqLite: true,
151           jQuery: true,
152           slice: true,
153           splice: true,
154           push: true,
155           toString: true,
156           ngMinErr: true,
157           angularModule: true,
158           uid: true,
159           REGEX_STRING_REGEXP: true,
160           VALIDITY_STATE_PROPERTY: true,
161
162           lowercase: true,
163           uppercase: true,
164           manualLowercase: true,
165           manualUppercase: true,
166           nodeName_: true,
167           isArrayLike: true,
168           forEach: true,
169           forEachSorted: true,
170           reverseParams: true,
171           nextUid: true,
172           setHashKey: true,
173           extend: true,
174           toInt: true,
175           inherit: true,
176           merge: true,
177           noop: true,
178           identity: true,
179           valueFn: true,
180           isUndefined: true,
181           isDefined: true,
182           isObject: true,
183           isBlankObject: true,
184           isString: true,
185           isNumber: true,
186           isDate: true,
187           isArray: true,
188           isFunction: true,
189           isRegExp: true,
190           isWindow: true,
191           isScope: true,
192           isFile: true,
193           isFormData: true,
194           isBlob: true,
195           isBoolean: true,
196           isPromiseLike: true,
197           trim: true,
198           escapeForRegexp: true,
199           isElement: true,
200           makeMap: true,
201           includes: true,
202           arrayRemove: true,
203           copy: true,
204           shallowCopy: true,
205           equals: true,
206           csp: true,
207           jq: true,
208           concat: true,
209           sliceArgs: true,
210           bind: true,
211           toJsonReplacer: true,
212           toJson: true,
213           fromJson: true,
214           convertTimezoneToLocal: true,
215           timezoneToOffset: true,
216           startingTag: true,
217           tryDecodeURIComponent: true,
218           parseKeyValue: true,
219           toKeyValue: true,
220           encodeUriSegment: true,
221           encodeUriQuery: true,
222           angularInit: true,
223           bootstrap: true,
224           getTestability: true,
225           snake_case: true,
226           bindJQuery: true,
227           assertArg: true,
228           assertArgFn: true,
229           assertNotHasOwnProperty: true,
230           getter: true,
231           getBlockNodes: true,
232           hasOwnProperty: true,
233           createMap: true,
234
235           NODE_TYPE_ELEMENT: true,
236           NODE_TYPE_ATTRIBUTE: true,
237           NODE_TYPE_TEXT: true,
238           NODE_TYPE_COMMENT: true,
239           NODE_TYPE_DOCUMENT: true,
240           NODE_TYPE_DOCUMENT_FRAGMENT: true,
241         */
242
243         ////////////////////////////////////
244
245         /**
246          * @ngdoc module
247          * @name ng
248          * @module ng
249          * @description
250          *
251          * # ng (core module)
252          * The ng module is loaded by default when an AngularJS application is started. The module itself
253          * contains the essential components for an AngularJS application to function. The table below
254          * lists a high level breakdown of each of the services/factories, filters, directives and testing
255          * components available within this core module.
256          *
257          * <div doc-module-components="ng"></div>
258          */
259
260         var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
261
262         // The name of a form control's ValidityState property.
263         // This is used so that it's possible for internal tests to create mock ValidityStates.
264         var VALIDITY_STATE_PROPERTY = 'validity';
265
266         /**
267          * @ngdoc function
268          * @name angular.lowercase
269          * @module ng
270          * @kind function
271          *
272          * @description Converts the specified string to lowercase.
273          * @param {string} string String to be converted to lowercase.
274          * @returns {string} Lowercased string.
275          */
276         var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
277         var hasOwnProperty = Object.prototype.hasOwnProperty;
278
279         /**
280          * @ngdoc function
281          * @name angular.uppercase
282          * @module ng
283          * @kind function
284          *
285          * @description Converts the specified string to uppercase.
286          * @param {string} string String to be converted to uppercase.
287          * @returns {string} Uppercased string.
288          */
289         var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
290
291
292         var manualLowercase = function(s) {
293           /* jshint bitwise: false */
294           return isString(s)
295               ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
296               : s;
297         };
298         var manualUppercase = function(s) {
299           /* jshint bitwise: false */
300           return isString(s)
301               ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
302               : s;
303         };
304
305
306         // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
307         // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
308         // with correct but slower alternatives.
309         if ('i' !== 'I'.toLowerCase()) {
310           lowercase = manualLowercase;
311           uppercase = manualUppercase;
312         }
313
314
315         var
316             msie,             // holds major version number for IE, or NaN if UA is not IE.
317             jqLite,           // delay binding since jQuery could be loaded after us.
318             jQuery,           // delay binding
319             slice             = [].slice,
320             splice            = [].splice,
321             push              = [].push,
322             toString          = Object.prototype.toString,
323             getPrototypeOf    = Object.getPrototypeOf,
324             ngMinErr          = minErr('ng'),
325
326             /** @name angular */
327             angular           = window.angular || (window.angular = {}),
328             angularModule,
329             uid               = 0;
330
331         /**
332          * documentMode is an IE-only property
333          * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
334          */
335         msie = document.documentMode;
336
337
338         /**
339          * @private
340          * @param {*} obj
341          * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
342          *                   String ...)
343          */
344         function isArrayLike(obj) {
345
346           // `null`, `undefined` and `window` are not array-like
347           if (obj == null || isWindow(obj)) return false;
348
349           // arrays, strings and jQuery/jqLite objects are array like
350           // * jqLite is either the jQuery or jqLite constructor function
351           // * we have to check the existance of jqLite first as this method is called
352           //   via the forEach method when constructing the jqLite object in the first place
353           if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
354
355           // Support: iOS 8.2 (not reproducible in simulator)
356           // "length" in obj used to prevent JIT error (gh-11508)
357           var length = "length" in Object(obj) && obj.length;
358
359           // NodeList objects (with `item` method) and
360           // other objects with suitable length characteristics are array-like
361           return isNumber(length) &&
362             (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
363         }
364
365         /**
366          * @ngdoc function
367          * @name angular.forEach
368          * @module ng
369          * @kind function
370          *
371          * @description
372          * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
373          * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
374          * is the value of an object property or an array element, `key` is the object property key or
375          * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
376          *
377          * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
378          * using the `hasOwnProperty` method.
379          *
380          * Unlike ES262's
381          * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
382          * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
383          * return the value provided.
384          *
385            ```js
386              var values = {name: 'misko', gender: 'male'};
387              var log = [];
388              angular.forEach(values, function(value, key) {
389                this.push(key + ': ' + value);
390              }, log);
391              expect(log).toEqual(['name: misko', 'gender: male']);
392            ```
393          *
394          * @param {Object|Array} obj Object to iterate over.
395          * @param {Function} iterator Iterator function.
396          * @param {Object=} context Object to become context (`this`) for the iterator function.
397          * @returns {Object|Array} Reference to `obj`.
398          */
399
400         function forEach(obj, iterator, context) {
401           var key, length;
402           if (obj) {
403             if (isFunction(obj)) {
404               for (key in obj) {
405                 // Need to check if hasOwnProperty exists,
406                 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
407                 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
408                   iterator.call(context, obj[key], key, obj);
409                 }
410               }
411             } else if (isArray(obj) || isArrayLike(obj)) {
412               var isPrimitive = typeof obj !== 'object';
413               for (key = 0, length = obj.length; key < length; key++) {
414                 if (isPrimitive || key in obj) {
415                   iterator.call(context, obj[key], key, obj);
416                 }
417               }
418             } else if (obj.forEach && obj.forEach !== forEach) {
419                 obj.forEach(iterator, context, obj);
420             } else if (isBlankObject(obj)) {
421               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
422               for (key in obj) {
423                 iterator.call(context, obj[key], key, obj);
424               }
425             } else if (typeof obj.hasOwnProperty === 'function') {
426               // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
427               for (key in obj) {
428                 if (obj.hasOwnProperty(key)) {
429                   iterator.call(context, obj[key], key, obj);
430                 }
431               }
432             } else {
433               // Slow path for objects which do not have a method `hasOwnProperty`
434               for (key in obj) {
435                 if (hasOwnProperty.call(obj, key)) {
436                   iterator.call(context, obj[key], key, obj);
437                 }
438               }
439             }
440           }
441           return obj;
442         }
443
444         function forEachSorted(obj, iterator, context) {
445           var keys = Object.keys(obj).sort();
446           for (var i = 0; i < keys.length; i++) {
447             iterator.call(context, obj[keys[i]], keys[i]);
448           }
449           return keys;
450         }
451
452
453         /**
454          * when using forEach the params are value, key, but it is often useful to have key, value.
455          * @param {function(string, *)} iteratorFn
456          * @returns {function(*, string)}
457          */
458         function reverseParams(iteratorFn) {
459           return function(value, key) { iteratorFn(key, value); };
460         }
461
462         /**
463          * A consistent way of creating unique IDs in angular.
464          *
465          * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
466          * we hit number precision issues in JavaScript.
467          *
468          * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
469          *
470          * @returns {number} an unique alpha-numeric string
471          */
472         function nextUid() {
473           return ++uid;
474         }
475
476
477         /**
478          * Set or clear the hashkey for an object.
479          * @param obj object
480          * @param h the hashkey (!truthy to delete the hashkey)
481          */
482         function setHashKey(obj, h) {
483           if (h) {
484             obj.$$hashKey = h;
485           } else {
486             delete obj.$$hashKey;
487           }
488         }
489
490
491         function baseExtend(dst, objs, deep) {
492           var h = dst.$$hashKey;
493
494           for (var i = 0, ii = objs.length; i < ii; ++i) {
495             var obj = objs[i];
496             if (!isObject(obj) && !isFunction(obj)) continue;
497             var keys = Object.keys(obj);
498             for (var j = 0, jj = keys.length; j < jj; j++) {
499               var key = keys[j];
500               var src = obj[key];
501
502               if (deep && isObject(src)) {
503                 if (isDate(src)) {
504                   dst[key] = new Date(src.valueOf());
505                 } else if (isRegExp(src)) {
506                   dst[key] = new RegExp(src);
507                 } else if (src.nodeName) {
508                   dst[key] = src.cloneNode(true);
509                 } else if (isElement(src)) {
510                   dst[key] = src.clone();
511                 } else {
512                   if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
513                   baseExtend(dst[key], [src], true);
514                 }
515               } else {
516                 dst[key] = src;
517               }
518             }
519           }
520
521           setHashKey(dst, h);
522           return dst;
523         }
524
525         /**
526          * @ngdoc function
527          * @name angular.extend
528          * @module ng
529          * @kind function
530          *
531          * @description
532          * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
533          * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
534          * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
535          *
536          * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
537          * {@link angular.merge} for this.
538          *
539          * @param {Object} dst Destination object.
540          * @param {...Object} src Source object(s).
541          * @returns {Object} Reference to `dst`.
542          */
543         function extend(dst) {
544           return baseExtend(dst, slice.call(arguments, 1), false);
545         }
546
547
548         /**
549         * @ngdoc function
550         * @name angular.merge
551         * @module ng
552         * @kind function
553         *
554         * @description
555         * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
556         * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
557         * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
558         *
559         * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
560         * objects, performing a deep copy.
561         *
562         * @param {Object} dst Destination object.
563         * @param {...Object} src Source object(s).
564         * @returns {Object} Reference to `dst`.
565         */
566         function merge(dst) {
567           return baseExtend(dst, slice.call(arguments, 1), true);
568         }
569
570
571
572         function toInt(str) {
573           return parseInt(str, 10);
574         }
575
576
577         function inherit(parent, extra) {
578           return extend(Object.create(parent), extra);
579         }
580
581         /**
582          * @ngdoc function
583          * @name angular.noop
584          * @module ng
585          * @kind function
586          *
587          * @description
588          * A function that performs no operations. This function can be useful when writing code in the
589          * functional style.
590            ```js
591              function foo(callback) {
592                var result = calculateResult();
593                (callback || angular.noop)(result);
594              }
595            ```
596          */
597         function noop() {}
598         noop.$inject = [];
599
600
601         /**
602          * @ngdoc function
603          * @name angular.identity
604          * @module ng
605          * @kind function
606          *
607          * @description
608          * A function that returns its first argument. This function is useful when writing code in the
609          * functional style.
610          *
611            ```js
612              function transformer(transformationFn, value) {
613                return (transformationFn || angular.identity)(value);
614              };
615            ```
616           * @param {*} value to be returned.
617           * @returns {*} the value passed in.
618          */
619         function identity($) {return $;}
620         identity.$inject = [];
621
622
623         function valueFn(value) {return function() {return value;};}
624
625         function hasCustomToString(obj) {
626           return isFunction(obj.toString) && obj.toString !== toString;
627         }
628
629
630         /**
631          * @ngdoc function
632          * @name angular.isUndefined
633          * @module ng
634          * @kind function
635          *
636          * @description
637          * Determines if a reference is undefined.
638          *
639          * @param {*} value Reference to check.
640          * @returns {boolean} True if `value` is undefined.
641          */
642         function isUndefined(value) {return typeof value === 'undefined';}
643
644
645         /**
646          * @ngdoc function
647          * @name angular.isDefined
648          * @module ng
649          * @kind function
650          *
651          * @description
652          * Determines if a reference is defined.
653          *
654          * @param {*} value Reference to check.
655          * @returns {boolean} True if `value` is defined.
656          */
657         function isDefined(value) {return typeof value !== 'undefined';}
658
659
660         /**
661          * @ngdoc function
662          * @name angular.isObject
663          * @module ng
664          * @kind function
665          *
666          * @description
667          * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
668          * considered to be objects. Note that JavaScript arrays are objects.
669          *
670          * @param {*} value Reference to check.
671          * @returns {boolean} True if `value` is an `Object` but not `null`.
672          */
673         function isObject(value) {
674           // http://jsperf.com/isobject4
675           return value !== null && typeof value === 'object';
676         }
677
678
679         /**
680          * Determine if a value is an object with a null prototype
681          *
682          * @returns {boolean} True if `value` is an `Object` with a null prototype
683          */
684         function isBlankObject(value) {
685           return value !== null && typeof value === 'object' && !getPrototypeOf(value);
686         }
687
688
689         /**
690          * @ngdoc function
691          * @name angular.isString
692          * @module ng
693          * @kind function
694          *
695          * @description
696          * Determines if a reference is a `String`.
697          *
698          * @param {*} value Reference to check.
699          * @returns {boolean} True if `value` is a `String`.
700          */
701         function isString(value) {return typeof value === 'string';}
702
703
704         /**
705          * @ngdoc function
706          * @name angular.isNumber
707          * @module ng
708          * @kind function
709          *
710          * @description
711          * Determines if a reference is a `Number`.
712          *
713          * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
714          *
715          * If you wish to exclude these then you can use the native
716          * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
717          * method.
718          *
719          * @param {*} value Reference to check.
720          * @returns {boolean} True if `value` is a `Number`.
721          */
722         function isNumber(value) {return typeof value === 'number';}
723
724
725         /**
726          * @ngdoc function
727          * @name angular.isDate
728          * @module ng
729          * @kind function
730          *
731          * @description
732          * Determines if a value is a date.
733          *
734          * @param {*} value Reference to check.
735          * @returns {boolean} True if `value` is a `Date`.
736          */
737         function isDate(value) {
738           return toString.call(value) === '[object Date]';
739         }
740
741
742         /**
743          * @ngdoc function
744          * @name angular.isArray
745          * @module ng
746          * @kind function
747          *
748          * @description
749          * Determines if a reference is an `Array`.
750          *
751          * @param {*} value Reference to check.
752          * @returns {boolean} True if `value` is an `Array`.
753          */
754         var isArray = Array.isArray;
755
756         /**
757          * @ngdoc function
758          * @name angular.isFunction
759          * @module ng
760          * @kind function
761          *
762          * @description
763          * Determines if a reference is a `Function`.
764          *
765          * @param {*} value Reference to check.
766          * @returns {boolean} True if `value` is a `Function`.
767          */
768         function isFunction(value) {return typeof value === 'function';}
769
770
771         /**
772          * Determines if a value is a regular expression object.
773          *
774          * @private
775          * @param {*} value Reference to check.
776          * @returns {boolean} True if `value` is a `RegExp`.
777          */
778         function isRegExp(value) {
779           return toString.call(value) === '[object RegExp]';
780         }
781
782
783         /**
784          * Checks if `obj` is a window object.
785          *
786          * @private
787          * @param {*} obj Object to check
788          * @returns {boolean} True if `obj` is a window obj.
789          */
790         function isWindow(obj) {
791           return obj && obj.window === obj;
792         }
793
794
795         function isScope(obj) {
796           return obj && obj.$evalAsync && obj.$watch;
797         }
798
799
800         function isFile(obj) {
801           return toString.call(obj) === '[object File]';
802         }
803
804
805         function isFormData(obj) {
806           return toString.call(obj) === '[object FormData]';
807         }
808
809
810         function isBlob(obj) {
811           return toString.call(obj) === '[object Blob]';
812         }
813
814
815         function isBoolean(value) {
816           return typeof value === 'boolean';
817         }
818
819
820         function isPromiseLike(obj) {
821           return obj && isFunction(obj.then);
822         }
823
824
825         var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
826         function isTypedArray(value) {
827           return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
828         }
829
830
831         var trim = function(value) {
832           return isString(value) ? value.trim() : value;
833         };
834
835         // Copied from:
836         // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
837         // Prereq: s is a string.
838         var escapeForRegexp = function(s) {
839           return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
840                    replace(/\x08/g, '\\x08');
841         };
842
843
844         /**
845          * @ngdoc function
846          * @name angular.isElement
847          * @module ng
848          * @kind function
849          *
850          * @description
851          * Determines if a reference is a DOM element (or wrapped jQuery element).
852          *
853          * @param {*} value Reference to check.
854          * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
855          */
856         function isElement(node) {
857           return !!(node &&
858             (node.nodeName  // we are a direct element
859             || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
860         }
861
862         /**
863          * @param str 'key1,key2,...'
864          * @returns {object} in the form of {key1:true, key2:true, ...}
865          */
866         function makeMap(str) {
867           var obj = {}, items = str.split(","), i;
868           for (i = 0; i < items.length; i++) {
869             obj[items[i]] = true;
870           }
871           return obj;
872         }
873
874
875         function nodeName_(element) {
876           return lowercase(element.nodeName || (element[0] && element[0].nodeName));
877         }
878
879         function includes(array, obj) {
880           return Array.prototype.indexOf.call(array, obj) != -1;
881         }
882
883         function arrayRemove(array, value) {
884           var index = array.indexOf(value);
885           if (index >= 0) {
886             array.splice(index, 1);
887           }
888           return index;
889         }
890
891         /**
892          * @ngdoc function
893          * @name angular.copy
894          * @module ng
895          * @kind function
896          *
897          * @description
898          * Creates a deep copy of `source`, which should be an object or an array.
899          *
900          * * If no destination is supplied, a copy of the object or array is created.
901          * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
902          *   are deleted and then all elements/properties from the source are copied to it.
903          * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
904          * * If `source` is identical to 'destination' an exception will be thrown.
905          *
906          * @param {*} source The source that will be used to make a copy.
907          *                   Can be any type, including primitives, `null`, and `undefined`.
908          * @param {(Object|Array)=} destination Destination into which the source is copied. If
909          *     provided, must be of the same type as `source`.
910          * @returns {*} The copy or updated `destination`, if `destination` was specified.
911          *
912          * @example
913          <example module="copyExample">
914          <file name="index.html">
915          <div ng-controller="ExampleController">
916          <form novalidate class="simple-form">
917          Name: <input type="text" ng-model="user.name" /><br />
918          E-mail: <input type="email" ng-model="user.email" /><br />
919          Gender: <input type="radio" ng-model="user.gender" value="male" />male
920          <input type="radio" ng-model="user.gender" value="female" />female<br />
921          <button ng-click="reset()">RESET</button>
922          <button ng-click="update(user)">SAVE</button>
923          </form>
924          <pre>form = {{user | json}}</pre>
925          <pre>master = {{master | json}}</pre>
926          </div>
927
928          <script>
929           angular.module('copyExample', [])
930             .controller('ExampleController', ['$scope', function($scope) {
931               $scope.master= {};
932
933               $scope.update = function(user) {
934                 // Example with 1 argument
935                 $scope.master= angular.copy(user);
936               };
937
938               $scope.reset = function() {
939                 // Example with 2 arguments
940                 angular.copy($scope.master, $scope.user);
941               };
942
943               $scope.reset();
944             }]);
945          </script>
946          </file>
947          </example>
948          */
949         function copy(source, destination) {
950           var stackSource = [];
951           var stackDest = [];
952
953           if (destination) {
954             if (isTypedArray(destination)) {
955               throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
956             }
957             if (source === destination) {
958               throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
959             }
960
961             // Empty the destination object
962             if (isArray(destination)) {
963               destination.length = 0;
964             } else {
965               forEach(destination, function(value, key) {
966                 if (key !== '$$hashKey') {
967                   delete destination[key];
968                 }
969               });
970             }
971
972             stackSource.push(source);
973             stackDest.push(destination);
974             return copyRecurse(source, destination);
975           }
976
977           return copyElement(source);
978
979           function copyRecurse(source, destination) {
980             var h = destination.$$hashKey;
981             var result, key;
982             if (isArray(source)) {
983               for (var i = 0, ii = source.length; i < ii; i++) {
984                 destination.push(copyElement(source[i]));
985               }
986             } else if (isBlankObject(source)) {
987               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
988               for (key in source) {
989                 destination[key] = copyElement(source[key]);
990               }
991             } else if (source && typeof source.hasOwnProperty === 'function') {
992               // Slow path, which must rely on hasOwnProperty
993               for (key in source) {
994                 if (source.hasOwnProperty(key)) {
995                   destination[key] = copyElement(source[key]);
996                 }
997               }
998             } else {
999               // Slowest path --- hasOwnProperty can't be called as a method
1000               for (key in source) {
1001                 if (hasOwnProperty.call(source, key)) {
1002                   destination[key] = copyElement(source[key]);
1003                 }
1004               }
1005             }
1006             setHashKey(destination, h);
1007             return destination;
1008           }
1009
1010           function copyElement(source) {
1011             // Simple values
1012             if (!isObject(source)) {
1013               return source;
1014             }
1015
1016             // Already copied values
1017             var index = stackSource.indexOf(source);
1018             if (index !== -1) {
1019               return stackDest[index];
1020             }
1021
1022             if (isWindow(source) || isScope(source)) {
1023               throw ngMinErr('cpws',
1024                 "Can't copy! Making copies of Window or Scope instances is not supported.");
1025             }
1026
1027             var needsRecurse = false;
1028             var destination;
1029
1030             if (isArray(source)) {
1031               destination = [];
1032               needsRecurse = true;
1033             } else if (isTypedArray(source)) {
1034               destination = new source.constructor(source);
1035             } else if (isDate(source)) {
1036               destination = new Date(source.getTime());
1037             } else if (isRegExp(source)) {
1038               destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1039               destination.lastIndex = source.lastIndex;
1040             } else if (isFunction(source.cloneNode)) {
1041                 destination = source.cloneNode(true);
1042             } else {
1043               destination = Object.create(getPrototypeOf(source));
1044               needsRecurse = true;
1045             }
1046
1047             stackSource.push(source);
1048             stackDest.push(destination);
1049
1050             return needsRecurse
1051               ? copyRecurse(source, destination)
1052               : destination;
1053           }
1054         }
1055
1056         /**
1057          * Creates a shallow copy of an object, an array or a primitive.
1058          *
1059          * Assumes that there are no proto properties for objects.
1060          */
1061         function shallowCopy(src, dst) {
1062           if (isArray(src)) {
1063             dst = dst || [];
1064
1065             for (var i = 0, ii = src.length; i < ii; i++) {
1066               dst[i] = src[i];
1067             }
1068           } else if (isObject(src)) {
1069             dst = dst || {};
1070
1071             for (var key in src) {
1072               if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1073                 dst[key] = src[key];
1074               }
1075             }
1076           }
1077
1078           return dst || src;
1079         }
1080
1081
1082         /**
1083          * @ngdoc function
1084          * @name angular.equals
1085          * @module ng
1086          * @kind function
1087          *
1088          * @description
1089          * Determines if two objects or two values are equivalent. Supports value types, regular
1090          * expressions, arrays and objects.
1091          *
1092          * Two objects or values are considered equivalent if at least one of the following is true:
1093          *
1094          * * Both objects or values pass `===` comparison.
1095          * * Both objects or values are of the same type and all of their properties are equal by
1096          *   comparing them with `angular.equals`.
1097          * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1098          * * Both values represent the same regular expression (In JavaScript,
1099          *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1100          *   representation matches).
1101          *
1102          * During a property comparison, properties of `function` type and properties with names
1103          * that begin with `$` are ignored.
1104          *
1105          * Scope and DOMWindow objects are being compared only by identify (`===`).
1106          *
1107          * @param {*} o1 Object or value to compare.
1108          * @param {*} o2 Object or value to compare.
1109          * @returns {boolean} True if arguments are equal.
1110          */
1111         function equals(o1, o2) {
1112           if (o1 === o2) return true;
1113           if (o1 === null || o2 === null) return false;
1114           if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1115           var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1116           if (t1 == t2) {
1117             if (t1 == 'object') {
1118               if (isArray(o1)) {
1119                 if (!isArray(o2)) return false;
1120                 if ((length = o1.length) == o2.length) {
1121                   for (key = 0; key < length; key++) {
1122                     if (!equals(o1[key], o2[key])) return false;
1123                   }
1124                   return true;
1125                 }
1126               } else if (isDate(o1)) {
1127                 if (!isDate(o2)) return false;
1128                 return equals(o1.getTime(), o2.getTime());
1129               } else if (isRegExp(o1)) {
1130                 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1131               } else {
1132                 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1133                   isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1134                 keySet = createMap();
1135                 for (key in o1) {
1136                   if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1137                   if (!equals(o1[key], o2[key])) return false;
1138                   keySet[key] = true;
1139                 }
1140                 for (key in o2) {
1141                   if (!(key in keySet) &&
1142                       key.charAt(0) !== '$' &&
1143                       isDefined(o2[key]) &&
1144                       !isFunction(o2[key])) return false;
1145                 }
1146                 return true;
1147               }
1148             }
1149           }
1150           return false;
1151         }
1152
1153         var csp = function() {
1154           if (!isDefined(csp.rules)) {
1155
1156
1157             var ngCspElement = (document.querySelector('[ng-csp]') ||
1158                             document.querySelector('[data-ng-csp]'));
1159
1160             if (ngCspElement) {
1161               var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1162                             ngCspElement.getAttribute('data-ng-csp');
1163               csp.rules = {
1164                 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1165                 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1166               };
1167             } else {
1168               csp.rules = {
1169                 noUnsafeEval: noUnsafeEval(),
1170                 noInlineStyle: false
1171               };
1172             }
1173           }
1174
1175           return csp.rules;
1176
1177           function noUnsafeEval() {
1178             try {
1179               /* jshint -W031, -W054 */
1180               new Function('');
1181               /* jshint +W031, +W054 */
1182               return false;
1183             } catch (e) {
1184               return true;
1185             }
1186           }
1187         };
1188
1189         /**
1190          * @ngdoc directive
1191          * @module ng
1192          * @name ngJq
1193          *
1194          * @element ANY
1195          * @param {string=} ngJq the name of the library available under `window`
1196          * to be used for angular.element
1197          * @description
1198          * Use this directive to force the angular.element library.  This should be
1199          * used to force either jqLite by leaving ng-jq blank or setting the name of
1200          * the jquery variable under window (eg. jQuery).
1201          *
1202          * Since angular looks for this directive when it is loaded (doesn't wait for the
1203          * DOMContentLoaded event), it must be placed on an element that comes before the script
1204          * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1205          * others ignored.
1206          *
1207          * @example
1208          * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1209          ```html
1210          <!doctype html>
1211          <html ng-app ng-jq>
1212          ...
1213          ...
1214          </html>
1215          ```
1216          * @example
1217          * This example shows how to use a jQuery based library of a different name.
1218          * The library name must be available at the top most 'window'.
1219          ```html
1220          <!doctype html>
1221          <html ng-app ng-jq="jQueryLib">
1222          ...
1223          ...
1224          </html>
1225          ```
1226          */
1227         var jq = function() {
1228           if (isDefined(jq.name_)) return jq.name_;
1229           var el;
1230           var i, ii = ngAttrPrefixes.length, prefix, name;
1231           for (i = 0; i < ii; ++i) {
1232             prefix = ngAttrPrefixes[i];
1233             if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1234               name = el.getAttribute(prefix + 'jq');
1235               break;
1236             }
1237           }
1238
1239           return (jq.name_ = name);
1240         };
1241
1242         function concat(array1, array2, index) {
1243           return array1.concat(slice.call(array2, index));
1244         }
1245
1246         function sliceArgs(args, startIndex) {
1247           return slice.call(args, startIndex || 0);
1248         }
1249
1250
1251         /* jshint -W101 */
1252         /**
1253          * @ngdoc function
1254          * @name angular.bind
1255          * @module ng
1256          * @kind function
1257          *
1258          * @description
1259          * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1260          * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1261          * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1262          * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1263          *
1264          * @param {Object} self Context which `fn` should be evaluated in.
1265          * @param {function()} fn Function to be bound.
1266          * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1267          * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1268          */
1269         /* jshint +W101 */
1270         function bind(self, fn) {
1271           var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1272           if (isFunction(fn) && !(fn instanceof RegExp)) {
1273             return curryArgs.length
1274               ? function() {
1275                   return arguments.length
1276                     ? fn.apply(self, concat(curryArgs, arguments, 0))
1277                     : fn.apply(self, curryArgs);
1278                 }
1279               : function() {
1280                   return arguments.length
1281                     ? fn.apply(self, arguments)
1282                     : fn.call(self);
1283                 };
1284           } else {
1285             // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1286             return fn;
1287           }
1288         }
1289
1290
1291         function toJsonReplacer(key, value) {
1292           var val = value;
1293
1294           if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1295             val = undefined;
1296           } else if (isWindow(value)) {
1297             val = '$WINDOW';
1298           } else if (value &&  document === value) {
1299             val = '$DOCUMENT';
1300           } else if (isScope(value)) {
1301             val = '$SCOPE';
1302           }
1303
1304           return val;
1305         }
1306
1307
1308         /**
1309          * @ngdoc function
1310          * @name angular.toJson
1311          * @module ng
1312          * @kind function
1313          *
1314          * @description
1315          * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1316          * stripped since angular uses this notation internally.
1317          *
1318          * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1319          * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1320          *    If set to an integer, the JSON output will contain that many spaces per indentation.
1321          * @returns {string|undefined} JSON-ified string representing `obj`.
1322          */
1323         function toJson(obj, pretty) {
1324           if (typeof obj === 'undefined') return undefined;
1325           if (!isNumber(pretty)) {
1326             pretty = pretty ? 2 : null;
1327           }
1328           return JSON.stringify(obj, toJsonReplacer, pretty);
1329         }
1330
1331
1332         /**
1333          * @ngdoc function
1334          * @name angular.fromJson
1335          * @module ng
1336          * @kind function
1337          *
1338          * @description
1339          * Deserializes a JSON string.
1340          *
1341          * @param {string} json JSON string to deserialize.
1342          * @returns {Object|Array|string|number} Deserialized JSON string.
1343          */
1344         function fromJson(json) {
1345           return isString(json)
1346               ? JSON.parse(json)
1347               : json;
1348         }
1349
1350
1351         function timezoneToOffset(timezone, fallback) {
1352           var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1353           return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1354         }
1355
1356
1357         function addDateMinutes(date, minutes) {
1358           date = new Date(date.getTime());
1359           date.setMinutes(date.getMinutes() + minutes);
1360           return date;
1361         }
1362
1363
1364         function convertTimezoneToLocal(date, timezone, reverse) {
1365           reverse = reverse ? -1 : 1;
1366           var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1367           return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1368         }
1369
1370
1371         /**
1372          * @returns {string} Returns the string representation of the element.
1373          */
1374         function startingTag(element) {
1375           element = jqLite(element).clone();
1376           try {
1377             // turns out IE does not let you set .html() on elements which
1378             // are not allowed to have children. So we just ignore it.
1379             element.empty();
1380           } catch (e) {}
1381           var elemHtml = jqLite('<div>').append(element).html();
1382           try {
1383             return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1384                 elemHtml.
1385                   match(/^(<[^>]+>)/)[1].
1386                   replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1387           } catch (e) {
1388             return lowercase(elemHtml);
1389           }
1390
1391         }
1392
1393
1394         /////////////////////////////////////////////////
1395
1396         /**
1397          * Tries to decode the URI component without throwing an exception.
1398          *
1399          * @private
1400          * @param str value potential URI component to check.
1401          * @returns {boolean} True if `value` can be decoded
1402          * with the decodeURIComponent function.
1403          */
1404         function tryDecodeURIComponent(value) {
1405           try {
1406             return decodeURIComponent(value);
1407           } catch (e) {
1408             // Ignore any invalid uri component
1409           }
1410         }
1411
1412
1413         /**
1414          * Parses an escaped url query string into key-value pairs.
1415          * @returns {Object.<string,boolean|Array>}
1416          */
1417         function parseKeyValue(/**string*/keyValue) {
1418           var obj = {};
1419           forEach((keyValue || "").split('&'), function(keyValue) {
1420             var splitPoint, key, val;
1421             if (keyValue) {
1422               key = keyValue = keyValue.replace(/\+/g,'%20');
1423               splitPoint = keyValue.indexOf('=');
1424               if (splitPoint !== -1) {
1425                 key = keyValue.substring(0, splitPoint);
1426                 val = keyValue.substring(splitPoint + 1);
1427               }
1428               key = tryDecodeURIComponent(key);
1429               if (isDefined(key)) {
1430                 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1431                 if (!hasOwnProperty.call(obj, key)) {
1432                   obj[key] = val;
1433                 } else if (isArray(obj[key])) {
1434                   obj[key].push(val);
1435                 } else {
1436                   obj[key] = [obj[key],val];
1437                 }
1438               }
1439             }
1440           });
1441           return obj;
1442         }
1443
1444         function toKeyValue(obj) {
1445           var parts = [];
1446           forEach(obj, function(value, key) {
1447             if (isArray(value)) {
1448               forEach(value, function(arrayValue) {
1449                 parts.push(encodeUriQuery(key, true) +
1450                            (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1451               });
1452             } else {
1453             parts.push(encodeUriQuery(key, true) +
1454                        (value === true ? '' : '=' + encodeUriQuery(value, true)));
1455             }
1456           });
1457           return parts.length ? parts.join('&') : '';
1458         }
1459
1460
1461         /**
1462          * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1463          * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1464          * segments:
1465          *    segment       = *pchar
1466          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1467          *    pct-encoded   = "%" HEXDIG HEXDIG
1468          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1469          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1470          *                     / "*" / "+" / "," / ";" / "="
1471          */
1472         function encodeUriSegment(val) {
1473           return encodeUriQuery(val, true).
1474                      replace(/%26/gi, '&').
1475                      replace(/%3D/gi, '=').
1476                      replace(/%2B/gi, '+');
1477         }
1478
1479
1480         /**
1481          * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1482          * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1483          * encoded per http://tools.ietf.org/html/rfc3986:
1484          *    query       = *( pchar / "/" / "?" )
1485          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1486          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1487          *    pct-encoded   = "%" HEXDIG HEXDIG
1488          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1489          *                     / "*" / "+" / "," / ";" / "="
1490          */
1491         function encodeUriQuery(val, pctEncodeSpaces) {
1492           return encodeURIComponent(val).
1493                      replace(/%40/gi, '@').
1494                      replace(/%3A/gi, ':').
1495                      replace(/%24/g, '$').
1496                      replace(/%2C/gi, ',').
1497                      replace(/%3B/gi, ';').
1498                      replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1499         }
1500
1501         var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1502
1503         function getNgAttribute(element, ngAttr) {
1504           var attr, i, ii = ngAttrPrefixes.length;
1505           for (i = 0; i < ii; ++i) {
1506             attr = ngAttrPrefixes[i] + ngAttr;
1507             if (isString(attr = element.getAttribute(attr))) {
1508               return attr;
1509             }
1510           }
1511           return null;
1512         }
1513
1514         /**
1515          * @ngdoc directive
1516          * @name ngApp
1517          * @module ng
1518          *
1519          * @element ANY
1520          * @param {angular.Module} ngApp an optional application
1521          *   {@link angular.module module} name to load.
1522          * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1523          *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1524          *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1525          *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1526          *   tracking down the root of these bugs.
1527          *
1528          * @description
1529          *
1530          * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1531          * designates the **root element** of the application and is typically placed near the root element
1532          * of the page - e.g. on the `<body>` or `<html>` tags.
1533          *
1534          * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1535          * found in the document will be used to define the root element to auto-bootstrap as an
1536          * application. To run multiple applications in an HTML document you must manually bootstrap them using
1537          * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1538          *
1539          * You can specify an **AngularJS module** to be used as the root module for the application.  This
1540          * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1541          * should contain the application code needed or have dependencies on other modules that will
1542          * contain the code. See {@link angular.module} for more information.
1543          *
1544          * In the example below if the `ngApp` directive were not placed on the `html` element then the
1545          * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1546          * would not be resolved to `3`.
1547          *
1548          * `ngApp` is the easiest, and most common way to bootstrap an application.
1549          *
1550          <example module="ngAppDemo">
1551            <file name="index.html">
1552            <div ng-controller="ngAppDemoController">
1553              I can add: {{a}} + {{b}} =  {{ a+b }}
1554            </div>
1555            </file>
1556            <file name="script.js">
1557            angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1558              $scope.a = 1;
1559              $scope.b = 2;
1560            });
1561            </file>
1562          </example>
1563          *
1564          * Using `ngStrictDi`, you would see something like this:
1565          *
1566          <example ng-app-included="true">
1567            <file name="index.html">
1568            <div ng-app="ngAppStrictDemo" ng-strict-di>
1569                <div ng-controller="GoodController1">
1570                    I can add: {{a}} + {{b}} =  {{ a+b }}
1571
1572                    <p>This renders because the controller does not fail to
1573                       instantiate, by using explicit annotation style (see
1574                       script.js for details)
1575                    </p>
1576                </div>
1577
1578                <div ng-controller="GoodController2">
1579                    Name: <input ng-model="name"><br />
1580                    Hello, {{name}}!
1581
1582                    <p>This renders because the controller does not fail to
1583                       instantiate, by using explicit annotation style
1584                       (see script.js for details)
1585                    </p>
1586                </div>
1587
1588                <div ng-controller="BadController">
1589                    I can add: {{a}} + {{b}} =  {{ a+b }}
1590
1591                    <p>The controller could not be instantiated, due to relying
1592                       on automatic function annotations (which are disabled in
1593                       strict mode). As such, the content of this section is not
1594                       interpolated, and there should be an error in your web console.
1595                    </p>
1596                </div>
1597            </div>
1598            </file>
1599            <file name="script.js">
1600            angular.module('ngAppStrictDemo', [])
1601              // BadController will fail to instantiate, due to relying on automatic function annotation,
1602              // rather than an explicit annotation
1603              .controller('BadController', function($scope) {
1604                $scope.a = 1;
1605                $scope.b = 2;
1606              })
1607              // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1608              // due to using explicit annotations using the array style and $inject property, respectively.
1609              .controller('GoodController1', ['$scope', function($scope) {
1610                $scope.a = 1;
1611                $scope.b = 2;
1612              }])
1613              .controller('GoodController2', GoodController2);
1614              function GoodController2($scope) {
1615                $scope.name = "World";
1616              }
1617              GoodController2.$inject = ['$scope'];
1618            </file>
1619            <file name="style.css">
1620            div[ng-controller] {
1621                margin-bottom: 1em;
1622                -webkit-border-radius: 4px;
1623                border-radius: 4px;
1624                border: 1px solid;
1625                padding: .5em;
1626            }
1627            div[ng-controller^=Good] {
1628                border-color: #d6e9c6;
1629                background-color: #dff0d8;
1630                color: #3c763d;
1631            }
1632            div[ng-controller^=Bad] {
1633                border-color: #ebccd1;
1634                background-color: #f2dede;
1635                color: #a94442;
1636                margin-bottom: 0;
1637            }
1638            </file>
1639          </example>
1640          */
1641         function angularInit(element, bootstrap) {
1642           var appElement,
1643               module,
1644               config = {};
1645
1646           // The element `element` has priority over any other element
1647           forEach(ngAttrPrefixes, function(prefix) {
1648             var name = prefix + 'app';
1649
1650             if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1651               appElement = element;
1652               module = element.getAttribute(name);
1653             }
1654           });
1655           forEach(ngAttrPrefixes, function(prefix) {
1656             var name = prefix + 'app';
1657             var candidate;
1658
1659             if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1660               appElement = candidate;
1661               module = candidate.getAttribute(name);
1662             }
1663           });
1664           if (appElement) {
1665             config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1666             bootstrap(appElement, module ? [module] : [], config);
1667           }
1668         }
1669
1670         /**
1671          * @ngdoc function
1672          * @name angular.bootstrap
1673          * @module ng
1674          * @description
1675          * Use this function to manually start up angular application.
1676          *
1677          * See: {@link guide/bootstrap Bootstrap}
1678          *
1679          * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1680          * They must use {@link ng.directive:ngApp ngApp}.
1681          *
1682          * Angular will detect if it has been loaded into the browser more than once and only allow the
1683          * first loaded script to be bootstrapped and will report a warning to the browser console for
1684          * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1685          * multiple instances of Angular try to work on the DOM.
1686          *
1687          * ```html
1688          * <!doctype html>
1689          * <html>
1690          * <body>
1691          * <div ng-controller="WelcomeController">
1692          *   {{greeting}}
1693          * </div>
1694          *
1695          * <script src="angular.js"></script>
1696          * <script>
1697          *   var app = angular.module('demo', [])
1698          *   .controller('WelcomeController', function($scope) {
1699          *       $scope.greeting = 'Welcome!';
1700          *   });
1701          *   angular.bootstrap(document, ['demo']);
1702          * </script>
1703          * </body>
1704          * </html>
1705          * ```
1706          *
1707          * @param {DOMElement} element DOM element which is the root of angular application.
1708          * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1709          *     Each item in the array should be the name of a predefined module or a (DI annotated)
1710          *     function that will be invoked by the injector as a `config` block.
1711          *     See: {@link angular.module modules}
1712          * @param {Object=} config an object for defining configuration options for the application. The
1713          *     following keys are supported:
1714          *
1715          * * `strictDi` - disable automatic function annotation for the application. This is meant to
1716          *   assist in finding bugs which break minified code. Defaults to `false`.
1717          *
1718          * @returns {auto.$injector} Returns the newly created injector for this app.
1719          */
1720         function bootstrap(element, modules, config) {
1721           if (!isObject(config)) config = {};
1722           var defaultConfig = {
1723             strictDi: false
1724           };
1725           config = extend(defaultConfig, config);
1726           var doBootstrap = function() {
1727             element = jqLite(element);
1728
1729             if (element.injector()) {
1730               var tag = (element[0] === document) ? 'document' : startingTag(element);
1731               //Encode angle brackets to prevent input from being sanitized to empty string #8683
1732               throw ngMinErr(
1733                   'btstrpd',
1734                   "App Already Bootstrapped with this Element '{0}'",
1735                   tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1736             }
1737
1738             modules = modules || [];
1739             modules.unshift(['$provide', function($provide) {
1740               $provide.value('$rootElement', element);
1741             }]);
1742
1743             if (config.debugInfoEnabled) {
1744               // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1745               modules.push(['$compileProvider', function($compileProvider) {
1746                 $compileProvider.debugInfoEnabled(true);
1747               }]);
1748             }
1749
1750             modules.unshift('ng');
1751             var injector = createInjector(modules, config.strictDi);
1752             injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1753                function bootstrapApply(scope, element, compile, injector) {
1754                 scope.$apply(function() {
1755                   element.data('$injector', injector);
1756                   compile(element)(scope);
1757                 });
1758               }]
1759             );
1760             return injector;
1761           };
1762
1763           var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1764           var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1765
1766           if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1767             config.debugInfoEnabled = true;
1768             window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1769           }
1770
1771           if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1772             return doBootstrap();
1773           }
1774
1775           window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1776           angular.resumeBootstrap = function(extraModules) {
1777             forEach(extraModules, function(module) {
1778               modules.push(module);
1779             });
1780             return doBootstrap();
1781           };
1782
1783           if (isFunction(angular.resumeDeferredBootstrap)) {
1784             angular.resumeDeferredBootstrap();
1785           }
1786         }
1787
1788         /**
1789          * @ngdoc function
1790          * @name angular.reloadWithDebugInfo
1791          * @module ng
1792          * @description
1793          * Use this function to reload the current application with debug information turned on.
1794          * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1795          *
1796          * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1797          */
1798         function reloadWithDebugInfo() {
1799           window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1800           window.location.reload();
1801         }
1802
1803         /**
1804          * @name angular.getTestability
1805          * @module ng
1806          * @description
1807          * Get the testability service for the instance of Angular on the given
1808          * element.
1809          * @param {DOMElement} element DOM element which is the root of angular application.
1810          */
1811         function getTestability(rootElement) {
1812           var injector = angular.element(rootElement).injector();
1813           if (!injector) {
1814             throw ngMinErr('test',
1815               'no injector found for element argument to getTestability');
1816           }
1817           return injector.get('$$testability');
1818         }
1819
1820         var SNAKE_CASE_REGEXP = /[A-Z]/g;
1821         function snake_case(name, separator) {
1822           separator = separator || '_';
1823           return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1824             return (pos ? separator : '') + letter.toLowerCase();
1825           });
1826         }
1827
1828         var bindJQueryFired = false;
1829         var skipDestroyOnNextJQueryCleanData;
1830         function bindJQuery() {
1831           var originalCleanData;
1832
1833           if (bindJQueryFired) {
1834             return;
1835           }
1836
1837           // bind to jQuery if present;
1838           var jqName = jq();
1839           jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1840                    !jqName             ? undefined     :   // use jqLite
1841                                          window[jqName];   // use jQuery specified by `ngJq`
1842
1843           // Use jQuery if it exists with proper functionality, otherwise default to us.
1844           // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1845           // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1846           // versions. It will not work for sure with jQuery <1.7, though.
1847           if (jQuery && jQuery.fn.on) {
1848             jqLite = jQuery;
1849             extend(jQuery.fn, {
1850               scope: JQLitePrototype.scope,
1851               isolateScope: JQLitePrototype.isolateScope,
1852               controller: JQLitePrototype.controller,
1853               injector: JQLitePrototype.injector,
1854               inheritedData: JQLitePrototype.inheritedData
1855             });
1856
1857             // All nodes removed from the DOM via various jQuery APIs like .remove()
1858             // are passed through jQuery.cleanData. Monkey-patch this method to fire
1859             // the $destroy event on all removed nodes.
1860             originalCleanData = jQuery.cleanData;
1861             jQuery.cleanData = function(elems) {
1862               var events;
1863               if (!skipDestroyOnNextJQueryCleanData) {
1864                 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1865                   events = jQuery._data(elem, "events");
1866                   if (events && events.$destroy) {
1867                     jQuery(elem).triggerHandler('$destroy');
1868                   }
1869                 }
1870               } else {
1871                 skipDestroyOnNextJQueryCleanData = false;
1872               }
1873               originalCleanData(elems);
1874             };
1875           } else {
1876             jqLite = JQLite;
1877           }
1878
1879           angular.element = jqLite;
1880
1881           // Prevent double-proxying.
1882           bindJQueryFired = true;
1883         }
1884
1885         /**
1886          * throw error if the argument is falsy.
1887          */
1888         function assertArg(arg, name, reason) {
1889           if (!arg) {
1890             throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1891           }
1892           return arg;
1893         }
1894
1895         function assertArgFn(arg, name, acceptArrayAnnotation) {
1896           if (acceptArrayAnnotation && isArray(arg)) {
1897               arg = arg[arg.length - 1];
1898           }
1899
1900           assertArg(isFunction(arg), name, 'not a function, got ' +
1901               (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1902           return arg;
1903         }
1904
1905         /**
1906          * throw error if the name given is hasOwnProperty
1907          * @param  {String} name    the name to test
1908          * @param  {String} context the context in which the name is used, such as module or directive
1909          */
1910         function assertNotHasOwnProperty(name, context) {
1911           if (name === 'hasOwnProperty') {
1912             throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1913           }
1914         }
1915
1916         /**
1917          * Return the value accessible from the object by path. Any undefined traversals are ignored
1918          * @param {Object} obj starting object
1919          * @param {String} path path to traverse
1920          * @param {boolean} [bindFnToScope=true]
1921          * @returns {Object} value as accessible by path
1922          */
1923         //TODO(misko): this function needs to be removed
1924         function getter(obj, path, bindFnToScope) {
1925           if (!path) return obj;
1926           var keys = path.split('.');
1927           var key;
1928           var lastInstance = obj;
1929           var len = keys.length;
1930
1931           for (var i = 0; i < len; i++) {
1932             key = keys[i];
1933             if (obj) {
1934               obj = (lastInstance = obj)[key];
1935             }
1936           }
1937           if (!bindFnToScope && isFunction(obj)) {
1938             return bind(lastInstance, obj);
1939           }
1940           return obj;
1941         }
1942
1943         /**
1944          * Return the DOM siblings between the first and last node in the given array.
1945          * @param {Array} array like object
1946          * @returns {Array} the inputted object or a jqLite collection containing the nodes
1947          */
1948         function getBlockNodes(nodes) {
1949           // TODO(perf): update `nodes` instead of creating a new object?
1950           var node = nodes[0];
1951           var endNode = nodes[nodes.length - 1];
1952           var blockNodes;
1953
1954           for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1955             if (blockNodes || nodes[i] !== node) {
1956               if (!blockNodes) {
1957                 blockNodes = jqLite(slice.call(nodes, 0, i));
1958               }
1959               blockNodes.push(node);
1960             }
1961           }
1962
1963           return blockNodes || nodes;
1964         }
1965
1966
1967         /**
1968          * Creates a new object without a prototype. This object is useful for lookup without having to
1969          * guard against prototypically inherited properties via hasOwnProperty.
1970          *
1971          * Related micro-benchmarks:
1972          * - http://jsperf.com/object-create2
1973          * - http://jsperf.com/proto-map-lookup/2
1974          * - http://jsperf.com/for-in-vs-object-keys2
1975          *
1976          * @returns {Object}
1977          */
1978         function createMap() {
1979           return Object.create(null);
1980         }
1981
1982         var NODE_TYPE_ELEMENT = 1;
1983         var NODE_TYPE_ATTRIBUTE = 2;
1984         var NODE_TYPE_TEXT = 3;
1985         var NODE_TYPE_COMMENT = 8;
1986         var NODE_TYPE_DOCUMENT = 9;
1987         var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1988
1989         /**
1990          * @ngdoc type
1991          * @name angular.Module
1992          * @module ng
1993          * @description
1994          *
1995          * Interface for configuring angular {@link angular.module modules}.
1996          */
1997
1998         function setupModuleLoader(window) {
1999
2000           var $injectorMinErr = minErr('$injector');
2001           var ngMinErr = minErr('ng');
2002
2003           function ensure(obj, name, factory) {
2004             return obj[name] || (obj[name] = factory());
2005           }
2006
2007           var angular = ensure(window, 'angular', Object);
2008
2009           // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2010           angular.$$minErr = angular.$$minErr || minErr;
2011
2012           return ensure(angular, 'module', function() {
2013             /** @type {Object.<string, angular.Module>} */
2014             var modules = {};
2015
2016             /**
2017              * @ngdoc function
2018              * @name angular.module
2019              * @module ng
2020              * @description
2021              *
2022              * The `angular.module` is a global place for creating, registering and retrieving Angular
2023              * modules.
2024              * All modules (angular core or 3rd party) that should be available to an application must be
2025              * registered using this mechanism.
2026              *
2027              * Passing one argument retrieves an existing {@link angular.Module},
2028              * whereas passing more than one argument creates a new {@link angular.Module}
2029              *
2030              *
2031              * # Module
2032              *
2033              * A module is a collection of services, directives, controllers, filters, and configuration information.
2034              * `angular.module` is used to configure the {@link auto.$injector $injector}.
2035              *
2036              * ```js
2037              * // Create a new module
2038              * var myModule = angular.module('myModule', []);
2039              *
2040              * // register a new service
2041              * myModule.value('appName', 'MyCoolApp');
2042              *
2043              * // configure existing services inside initialization blocks.
2044              * myModule.config(['$locationProvider', function($locationProvider) {
2045              *   // Configure existing providers
2046              *   $locationProvider.hashPrefix('!');
2047              * }]);
2048              * ```
2049              *
2050              * Then you can create an injector and load your modules like this:
2051              *
2052              * ```js
2053              * var injector = angular.injector(['ng', 'myModule'])
2054              * ```
2055              *
2056              * However it's more likely that you'll just use
2057              * {@link ng.directive:ngApp ngApp} or
2058              * {@link angular.bootstrap} to simplify this process for you.
2059              *
2060              * @param {!string} name The name of the module to create or retrieve.
2061              * @param {!Array.<string>=} requires If specified then new module is being created. If
2062              *        unspecified then the module is being retrieved for further configuration.
2063              * @param {Function=} configFn Optional configuration function for the module. Same as
2064              *        {@link angular.Module#config Module#config()}.
2065              * @returns {module} new module with the {@link angular.Module} api.
2066              */
2067             return function module(name, requires, configFn) {
2068               var assertNotHasOwnProperty = function(name, context) {
2069                 if (name === 'hasOwnProperty') {
2070                   throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2071                 }
2072               };
2073
2074               assertNotHasOwnProperty(name, 'module');
2075               if (requires && modules.hasOwnProperty(name)) {
2076                 modules[name] = null;
2077               }
2078               return ensure(modules, name, function() {
2079                 if (!requires) {
2080                   throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2081                      "the module name or forgot to load it. If registering a module ensure that you " +
2082                      "specify the dependencies as the second argument.", name);
2083                 }
2084
2085                 /** @type {!Array.<Array.<*>>} */
2086                 var invokeQueue = [];
2087
2088                 /** @type {!Array.<Function>} */
2089                 var configBlocks = [];
2090
2091                 /** @type {!Array.<Function>} */
2092                 var runBlocks = [];
2093
2094                 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2095
2096                 /** @type {angular.Module} */
2097                 var moduleInstance = {
2098                   // Private state
2099                   _invokeQueue: invokeQueue,
2100                   _configBlocks: configBlocks,
2101                   _runBlocks: runBlocks,
2102
2103                   /**
2104                    * @ngdoc property
2105                    * @name angular.Module#requires
2106                    * @module ng
2107                    *
2108                    * @description
2109                    * Holds the list of modules which the injector will load before the current module is
2110                    * loaded.
2111                    */
2112                   requires: requires,
2113
2114                   /**
2115                    * @ngdoc property
2116                    * @name angular.Module#name
2117                    * @module ng
2118                    *
2119                    * @description
2120                    * Name of the module.
2121                    */
2122                   name: name,
2123
2124
2125                   /**
2126                    * @ngdoc method
2127                    * @name angular.Module#provider
2128                    * @module ng
2129                    * @param {string} name service name
2130                    * @param {Function} providerType Construction function for creating new instance of the
2131                    *                                service.
2132                    * @description
2133                    * See {@link auto.$provide#provider $provide.provider()}.
2134                    */
2135                   provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2136
2137                   /**
2138                    * @ngdoc method
2139                    * @name angular.Module#factory
2140                    * @module ng
2141                    * @param {string} name service name
2142                    * @param {Function} providerFunction Function for creating new instance of the service.
2143                    * @description
2144                    * See {@link auto.$provide#factory $provide.factory()}.
2145                    */
2146                   factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2147
2148                   /**
2149                    * @ngdoc method
2150                    * @name angular.Module#service
2151                    * @module ng
2152                    * @param {string} name service name
2153                    * @param {Function} constructor A constructor function that will be instantiated.
2154                    * @description
2155                    * See {@link auto.$provide#service $provide.service()}.
2156                    */
2157                   service: invokeLaterAndSetModuleName('$provide', 'service'),
2158
2159                   /**
2160                    * @ngdoc method
2161                    * @name angular.Module#value
2162                    * @module ng
2163                    * @param {string} name service name
2164                    * @param {*} object Service instance object.
2165                    * @description
2166                    * See {@link auto.$provide#value $provide.value()}.
2167                    */
2168                   value: invokeLater('$provide', 'value'),
2169
2170                   /**
2171                    * @ngdoc method
2172                    * @name angular.Module#constant
2173                    * @module ng
2174                    * @param {string} name constant name
2175                    * @param {*} object Constant value.
2176                    * @description
2177                    * Because the constants are fixed, they get applied before other provide methods.
2178                    * See {@link auto.$provide#constant $provide.constant()}.
2179                    */
2180                   constant: invokeLater('$provide', 'constant', 'unshift'),
2181
2182                    /**
2183                    * @ngdoc method
2184                    * @name angular.Module#decorator
2185                    * @module ng
2186                    * @param {string} The name of the service to decorate.
2187                    * @param {Function} This function will be invoked when the service needs to be
2188                    *                                    instantiated and should return the decorated service instance.
2189                    * @description
2190                    * See {@link auto.$provide#decorator $provide.decorator()}.
2191                    */
2192                   decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2193
2194                   /**
2195                    * @ngdoc method
2196                    * @name angular.Module#animation
2197                    * @module ng
2198                    * @param {string} name animation name
2199                    * @param {Function} animationFactory Factory function for creating new instance of an
2200                    *                                    animation.
2201                    * @description
2202                    *
2203                    * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2204                    *
2205                    *
2206                    * Defines an animation hook that can be later used with
2207                    * {@link $animate $animate} service and directives that use this service.
2208                    *
2209                    * ```js
2210                    * module.animation('.animation-name', function($inject1, $inject2) {
2211                    *   return {
2212                    *     eventName : function(element, done) {
2213                    *       //code to run the animation
2214                    *       //once complete, then run done()
2215                    *       return function cancellationFunction(element) {
2216                    *         //code to cancel the animation
2217                    *       }
2218                    *     }
2219                    *   }
2220                    * })
2221                    * ```
2222                    *
2223                    * See {@link ng.$animateProvider#register $animateProvider.register()} and
2224                    * {@link ngAnimate ngAnimate module} for more information.
2225                    */
2226                   animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2227
2228                   /**
2229                    * @ngdoc method
2230                    * @name angular.Module#filter
2231                    * @module ng
2232                    * @param {string} name Filter name - this must be a valid angular expression identifier
2233                    * @param {Function} filterFactory Factory function for creating new instance of filter.
2234                    * @description
2235                    * See {@link ng.$filterProvider#register $filterProvider.register()}.
2236                    *
2237                    * <div class="alert alert-warning">
2238                    * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2239                    * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2240                    * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2241                    * (`myapp_subsection_filterx`).
2242                    * </div>
2243                    */
2244                   filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2245
2246                   /**
2247                    * @ngdoc method
2248                    * @name angular.Module#controller
2249                    * @module ng
2250                    * @param {string|Object} name Controller name, or an object map of controllers where the
2251                    *    keys are the names and the values are the constructors.
2252                    * @param {Function} constructor Controller constructor function.
2253                    * @description
2254                    * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2255                    */
2256                   controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2257
2258                   /**
2259                    * @ngdoc method
2260                    * @name angular.Module#directive
2261                    * @module ng
2262                    * @param {string|Object} name Directive name, or an object map of directives where the
2263                    *    keys are the names and the values are the factories.
2264                    * @param {Function} directiveFactory Factory function for creating new instance of
2265                    * directives.
2266                    * @description
2267                    * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2268                    */
2269                   directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2270
2271                   /**
2272                    * @ngdoc method
2273                    * @name angular.Module#config
2274                    * @module ng
2275                    * @param {Function} configFn Execute this function on module load. Useful for service
2276                    *    configuration.
2277                    * @description
2278                    * Use this method to register work which needs to be performed on module loading.
2279                    * For more about how to configure services, see
2280                    * {@link providers#provider-recipe Provider Recipe}.
2281                    */
2282                   config: config,
2283
2284                   /**
2285                    * @ngdoc method
2286                    * @name angular.Module#run
2287                    * @module ng
2288                    * @param {Function} initializationFn Execute this function after injector creation.
2289                    *    Useful for application initialization.
2290                    * @description
2291                    * Use this method to register work which should be performed when the injector is done
2292                    * loading all modules.
2293                    */
2294                   run: function(block) {
2295                     runBlocks.push(block);
2296                     return this;
2297                   }
2298                 };
2299
2300                 if (configFn) {
2301                   config(configFn);
2302                 }
2303
2304                 return moduleInstance;
2305
2306                 /**
2307                  * @param {string} provider
2308                  * @param {string} method
2309                  * @param {String=} insertMethod
2310                  * @returns {angular.Module}
2311                  */
2312                 function invokeLater(provider, method, insertMethod, queue) {
2313                   if (!queue) queue = invokeQueue;
2314                   return function() {
2315                     queue[insertMethod || 'push']([provider, method, arguments]);
2316                     return moduleInstance;
2317                   };
2318                 }
2319
2320                 /**
2321                  * @param {string} provider
2322                  * @param {string} method
2323                  * @returns {angular.Module}
2324                  */
2325                 function invokeLaterAndSetModuleName(provider, method) {
2326                   return function(recipeName, factoryFunction) {
2327                     if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2328                     invokeQueue.push([provider, method, arguments]);
2329                     return moduleInstance;
2330                   };
2331                 }
2332               });
2333             };
2334           });
2335
2336         }
2337
2338         /* global: toDebugString: true */
2339
2340         function serializeObject(obj) {
2341           var seen = [];
2342
2343           return JSON.stringify(obj, function(key, val) {
2344             val = toJsonReplacer(key, val);
2345             if (isObject(val)) {
2346
2347               if (seen.indexOf(val) >= 0) return '...';
2348
2349               seen.push(val);
2350             }
2351             return val;
2352           });
2353         }
2354
2355         function toDebugString(obj) {
2356           if (typeof obj === 'function') {
2357             return obj.toString().replace(/ \{[\s\S]*$/, '');
2358           } else if (isUndefined(obj)) {
2359             return 'undefined';
2360           } else if (typeof obj !== 'string') {
2361             return serializeObject(obj);
2362           }
2363           return obj;
2364         }
2365
2366         /* global angularModule: true,
2367           version: true,
2368
2369           $CompileProvider,
2370
2371           htmlAnchorDirective,
2372           inputDirective,
2373           inputDirective,
2374           formDirective,
2375           scriptDirective,
2376           selectDirective,
2377           styleDirective,
2378           optionDirective,
2379           ngBindDirective,
2380           ngBindHtmlDirective,
2381           ngBindTemplateDirective,
2382           ngClassDirective,
2383           ngClassEvenDirective,
2384           ngClassOddDirective,
2385           ngCloakDirective,
2386           ngControllerDirective,
2387           ngFormDirective,
2388           ngHideDirective,
2389           ngIfDirective,
2390           ngIncludeDirective,
2391           ngIncludeFillContentDirective,
2392           ngInitDirective,
2393           ngNonBindableDirective,
2394           ngPluralizeDirective,
2395           ngRepeatDirective,
2396           ngShowDirective,
2397           ngStyleDirective,
2398           ngSwitchDirective,
2399           ngSwitchWhenDirective,
2400           ngSwitchDefaultDirective,
2401           ngOptionsDirective,
2402           ngTranscludeDirective,
2403           ngModelDirective,
2404           ngListDirective,
2405           ngChangeDirective,
2406           patternDirective,
2407           patternDirective,
2408           requiredDirective,
2409           requiredDirective,
2410           minlengthDirective,
2411           minlengthDirective,
2412           maxlengthDirective,
2413           maxlengthDirective,
2414           ngValueDirective,
2415           ngModelOptionsDirective,
2416           ngAttributeAliasDirectives,
2417           ngEventDirectives,
2418
2419           $AnchorScrollProvider,
2420           $AnimateProvider,
2421           $CoreAnimateCssProvider,
2422           $$CoreAnimateQueueProvider,
2423           $$CoreAnimateRunnerProvider,
2424           $BrowserProvider,
2425           $CacheFactoryProvider,
2426           $ControllerProvider,
2427           $DocumentProvider,
2428           $ExceptionHandlerProvider,
2429           $FilterProvider,
2430           $$ForceReflowProvider,
2431           $InterpolateProvider,
2432           $IntervalProvider,
2433           $$HashMapProvider,
2434           $HttpProvider,
2435           $HttpParamSerializerProvider,
2436           $HttpParamSerializerJQLikeProvider,
2437           $HttpBackendProvider,
2438           $xhrFactoryProvider,
2439           $LocationProvider,
2440           $LogProvider,
2441           $ParseProvider,
2442           $RootScopeProvider,
2443           $QProvider,
2444           $$QProvider,
2445           $$SanitizeUriProvider,
2446           $SceProvider,
2447           $SceDelegateProvider,
2448           $SnifferProvider,
2449           $TemplateCacheProvider,
2450           $TemplateRequestProvider,
2451           $$TestabilityProvider,
2452           $TimeoutProvider,
2453           $$RAFProvider,
2454           $WindowProvider,
2455           $$jqLiteProvider,
2456           $$CookieReaderProvider
2457         */
2458
2459
2460         /**
2461          * @ngdoc object
2462          * @name angular.version
2463          * @module ng
2464          * @description
2465          * An object that contains information about the current AngularJS version.
2466          *
2467          * This object has the following properties:
2468          *
2469          * - `full` – `{string}` – Full version string, such as "0.9.18".
2470          * - `major` – `{number}` – Major version number, such as "0".
2471          * - `minor` – `{number}` – Minor version number, such as "9".
2472          * - `dot` – `{number}` – Dot version number, such as "18".
2473          * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2474          */
2475         var version = {
2476           full: '1.4.8',    // all of these placeholder strings will be replaced by grunt's
2477           major: 1,    // package task
2478           minor: 4,
2479           dot: 8,
2480           codeName: 'ice-manipulation'
2481         };
2482
2483
2484         function publishExternalAPI(angular) {
2485           extend(angular, {
2486             'bootstrap': bootstrap,
2487             'copy': copy,
2488             'extend': extend,
2489             'merge': merge,
2490             'equals': equals,
2491             'element': jqLite,
2492             'forEach': forEach,
2493             'injector': createInjector,
2494             'noop': noop,
2495             'bind': bind,
2496             'toJson': toJson,
2497             'fromJson': fromJson,
2498             'identity': identity,
2499             'isUndefined': isUndefined,
2500             'isDefined': isDefined,
2501             'isString': isString,
2502             'isFunction': isFunction,
2503             'isObject': isObject,
2504             'isNumber': isNumber,
2505             'isElement': isElement,
2506             'isArray': isArray,
2507             'version': version,
2508             'isDate': isDate,
2509             'lowercase': lowercase,
2510             'uppercase': uppercase,
2511             'callbacks': {counter: 0},
2512             'getTestability': getTestability,
2513             '$$minErr': minErr,
2514             '$$csp': csp,
2515             'reloadWithDebugInfo': reloadWithDebugInfo
2516           });
2517
2518           angularModule = setupModuleLoader(window);
2519
2520           angularModule('ng', ['ngLocale'], ['$provide',
2521             function ngModule($provide) {
2522               // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2523               $provide.provider({
2524                 $$sanitizeUri: $$SanitizeUriProvider
2525               });
2526               $provide.provider('$compile', $CompileProvider).
2527                 directive({
2528                     a: htmlAnchorDirective,
2529                     input: inputDirective,
2530                     textarea: inputDirective,
2531                     form: formDirective,
2532                     script: scriptDirective,
2533                     select: selectDirective,
2534                     style: styleDirective,
2535                     option: optionDirective,
2536                     ngBind: ngBindDirective,
2537                     ngBindHtml: ngBindHtmlDirective,
2538                     ngBindTemplate: ngBindTemplateDirective,
2539                     ngClass: ngClassDirective,
2540                     ngClassEven: ngClassEvenDirective,
2541                     ngClassOdd: ngClassOddDirective,
2542                     ngCloak: ngCloakDirective,
2543                     ngController: ngControllerDirective,
2544                     ngForm: ngFormDirective,
2545                     ngHide: ngHideDirective,
2546                     ngIf: ngIfDirective,
2547                     ngInclude: ngIncludeDirective,
2548                     ngInit: ngInitDirective,
2549                     ngNonBindable: ngNonBindableDirective,
2550                     ngPluralize: ngPluralizeDirective,
2551                     ngRepeat: ngRepeatDirective,
2552                     ngShow: ngShowDirective,
2553                     ngStyle: ngStyleDirective,
2554                     ngSwitch: ngSwitchDirective,
2555                     ngSwitchWhen: ngSwitchWhenDirective,
2556                     ngSwitchDefault: ngSwitchDefaultDirective,
2557                     ngOptions: ngOptionsDirective,
2558                     ngTransclude: ngTranscludeDirective,
2559                     ngModel: ngModelDirective,
2560                     ngList: ngListDirective,
2561                     ngChange: ngChangeDirective,
2562                     pattern: patternDirective,
2563                     ngPattern: patternDirective,
2564                     required: requiredDirective,
2565                     ngRequired: requiredDirective,
2566                     minlength: minlengthDirective,
2567                     ngMinlength: minlengthDirective,
2568                     maxlength: maxlengthDirective,
2569                     ngMaxlength: maxlengthDirective,
2570                     ngValue: ngValueDirective,
2571                     ngModelOptions: ngModelOptionsDirective
2572                 }).
2573                 directive({
2574                   ngInclude: ngIncludeFillContentDirective
2575                 }).
2576                 directive(ngAttributeAliasDirectives).
2577                 directive(ngEventDirectives);
2578               $provide.provider({
2579                 $anchorScroll: $AnchorScrollProvider,
2580                 $animate: $AnimateProvider,
2581                 $animateCss: $CoreAnimateCssProvider,
2582                 $$animateQueue: $$CoreAnimateQueueProvider,
2583                 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2584                 $browser: $BrowserProvider,
2585                 $cacheFactory: $CacheFactoryProvider,
2586                 $controller: $ControllerProvider,
2587                 $document: $DocumentProvider,
2588                 $exceptionHandler: $ExceptionHandlerProvider,
2589                 $filter: $FilterProvider,
2590                 $$forceReflow: $$ForceReflowProvider,
2591                 $interpolate: $InterpolateProvider,
2592                 $interval: $IntervalProvider,
2593                 $http: $HttpProvider,
2594                 $httpParamSerializer: $HttpParamSerializerProvider,
2595                 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2596                 $httpBackend: $HttpBackendProvider,
2597                 $xhrFactory: $xhrFactoryProvider,
2598                 $location: $LocationProvider,
2599                 $log: $LogProvider,
2600                 $parse: $ParseProvider,
2601                 $rootScope: $RootScopeProvider,
2602                 $q: $QProvider,
2603                 $$q: $$QProvider,
2604                 $sce: $SceProvider,
2605                 $sceDelegate: $SceDelegateProvider,
2606                 $sniffer: $SnifferProvider,
2607                 $templateCache: $TemplateCacheProvider,
2608                 $templateRequest: $TemplateRequestProvider,
2609                 $$testability: $$TestabilityProvider,
2610                 $timeout: $TimeoutProvider,
2611                 $window: $WindowProvider,
2612                 $$rAF: $$RAFProvider,
2613                 $$jqLite: $$jqLiteProvider,
2614                 $$HashMap: $$HashMapProvider,
2615                 $$cookieReader: $$CookieReaderProvider
2616               });
2617             }
2618           ]);
2619         }
2620
2621         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2622          *     Any commits to this file should be reviewed with security in mind.  *
2623          *   Changes to this file can potentially create security vulnerabilities. *
2624          *          An approval from 2 Core members with history of modifying      *
2625          *                         this file is required.                          *
2626          *                                                                         *
2627          *  Does the change somehow allow for arbitrary javascript to be executed? *
2628          *    Or allows for someone to change the prototype of built-in objects?   *
2629          *     Or gives undesired access to variables likes document or window?    *
2630          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2631
2632         /* global JQLitePrototype: true,
2633           addEventListenerFn: true,
2634           removeEventListenerFn: true,
2635           BOOLEAN_ATTR: true,
2636           ALIASED_ATTR: true,
2637         */
2638
2639         //////////////////////////////////
2640         //JQLite
2641         //////////////////////////////////
2642
2643         /**
2644          * @ngdoc function
2645          * @name angular.element
2646          * @module ng
2647          * @kind function
2648          *
2649          * @description
2650          * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2651          *
2652          * If jQuery is available, `angular.element` is an alias for the
2653          * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2654          * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2655          *
2656          * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2657          * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2658          * commonly needed functionality with the goal of having a very small footprint.</div>
2659          *
2660          * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2661          *
2662          * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2663          * jqLite; they are never raw DOM references.</div>
2664          *
2665          * ## Angular's jqLite
2666          * jqLite provides only the following jQuery methods:
2667          *
2668          * - [`addClass()`](http://api.jquery.com/addClass/)
2669          * - [`after()`](http://api.jquery.com/after/)
2670          * - [`append()`](http://api.jquery.com/append/)
2671          * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2672          * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2673          * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2674          * - [`clone()`](http://api.jquery.com/clone/)
2675          * - [`contents()`](http://api.jquery.com/contents/)
2676          * - [`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'.
2677          * - [`data()`](http://api.jquery.com/data/)
2678          * - [`detach()`](http://api.jquery.com/detach/)
2679          * - [`empty()`](http://api.jquery.com/empty/)
2680          * - [`eq()`](http://api.jquery.com/eq/)
2681          * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2682          * - [`hasClass()`](http://api.jquery.com/hasClass/)
2683          * - [`html()`](http://api.jquery.com/html/)
2684          * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2685          * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2686          * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2687          * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2688          * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2689          * - [`prepend()`](http://api.jquery.com/prepend/)
2690          * - [`prop()`](http://api.jquery.com/prop/)
2691          * - [`ready()`](http://api.jquery.com/ready/)
2692          * - [`remove()`](http://api.jquery.com/remove/)
2693          * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2694          * - [`removeClass()`](http://api.jquery.com/removeClass/)
2695          * - [`removeData()`](http://api.jquery.com/removeData/)
2696          * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2697          * - [`text()`](http://api.jquery.com/text/)
2698          * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2699          * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2700          * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2701          * - [`val()`](http://api.jquery.com/val/)
2702          * - [`wrap()`](http://api.jquery.com/wrap/)
2703          *
2704          * ## jQuery/jqLite Extras
2705          * Angular also provides the following additional methods and events to both jQuery and jqLite:
2706          *
2707          * ### Events
2708          * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2709          *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2710          *    element before it is removed.
2711          *
2712          * ### Methods
2713          * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2714          *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2715          *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2716          *   `'ngModel'`).
2717          * - `injector()` - retrieves the injector of the current element or its parent.
2718          * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2719          *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2720          *   be enabled.
2721          * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2722          *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2723          *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2724          *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2725          * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2726          *   parent element is reached.
2727          *
2728          * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2729          * @returns {Object} jQuery object.
2730          */
2731
2732         JQLite.expando = 'ng339';
2733
2734         var jqCache = JQLite.cache = {},
2735             jqId = 1,
2736             addEventListenerFn = function(element, type, fn) {
2737               element.addEventListener(type, fn, false);
2738             },
2739             removeEventListenerFn = function(element, type, fn) {
2740               element.removeEventListener(type, fn, false);
2741             };
2742
2743         /*
2744          * !!! This is an undocumented "private" function !!!
2745          */
2746         JQLite._data = function(node) {
2747           //jQuery always returns an object on cache miss
2748           return this.cache[node[this.expando]] || {};
2749         };
2750
2751         function jqNextId() { return ++jqId; }
2752
2753
2754         var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2755         var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2756         var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2757         var jqLiteMinErr = minErr('jqLite');
2758
2759         /**
2760          * Converts snake_case to camelCase.
2761          * Also there is special case for Moz prefix starting with upper case letter.
2762          * @param name Name to normalize
2763          */
2764         function camelCase(name) {
2765           return name.
2766             replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2767               return offset ? letter.toUpperCase() : letter;
2768             }).
2769             replace(MOZ_HACK_REGEXP, 'Moz$1');
2770         }
2771
2772         var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2773         var HTML_REGEXP = /<|&#?\w+;/;
2774         var TAG_NAME_REGEXP = /<([\w:-]+)/;
2775         var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2776
2777         var wrapMap = {
2778           'option': [1, '<select multiple="multiple">', '</select>'],
2779
2780           'thead': [1, '<table>', '</table>'],
2781           'col': [2, '<table><colgroup>', '</colgroup></table>'],
2782           'tr': [2, '<table><tbody>', '</tbody></table>'],
2783           'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2784           '_default': [0, "", ""]
2785         };
2786
2787         wrapMap.optgroup = wrapMap.option;
2788         wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2789         wrapMap.th = wrapMap.td;
2790
2791
2792         function jqLiteIsTextNode(html) {
2793           return !HTML_REGEXP.test(html);
2794         }
2795
2796         function jqLiteAcceptsData(node) {
2797           // The window object can accept data but has no nodeType
2798           // Otherwise we are only interested in elements (1) and documents (9)
2799           var nodeType = node.nodeType;
2800           return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2801         }
2802
2803         function jqLiteHasData(node) {
2804           for (var key in jqCache[node.ng339]) {
2805             return true;
2806           }
2807           return false;
2808         }
2809
2810         function jqLiteBuildFragment(html, context) {
2811           var tmp, tag, wrap,
2812               fragment = context.createDocumentFragment(),
2813               nodes = [], i;
2814
2815           if (jqLiteIsTextNode(html)) {
2816             // Convert non-html into a text node
2817             nodes.push(context.createTextNode(html));
2818           } else {
2819             // Convert html into DOM nodes
2820             tmp = tmp || fragment.appendChild(context.createElement("div"));
2821             tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2822             wrap = wrapMap[tag] || wrapMap._default;
2823             tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2824
2825             // Descend through wrappers to the right content
2826             i = wrap[0];
2827             while (i--) {
2828               tmp = tmp.lastChild;
2829             }
2830
2831             nodes = concat(nodes, tmp.childNodes);
2832
2833             tmp = fragment.firstChild;
2834             tmp.textContent = "";
2835           }
2836
2837           // Remove wrapper from fragment
2838           fragment.textContent = "";
2839           fragment.innerHTML = ""; // Clear inner HTML
2840           forEach(nodes, function(node) {
2841             fragment.appendChild(node);
2842           });
2843
2844           return fragment;
2845         }
2846
2847         function jqLiteParseHTML(html, context) {
2848           context = context || document;
2849           var parsed;
2850
2851           if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2852             return [context.createElement(parsed[1])];
2853           }
2854
2855           if ((parsed = jqLiteBuildFragment(html, context))) {
2856             return parsed.childNodes;
2857           }
2858
2859           return [];
2860         }
2861
2862
2863         // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2864         var jqLiteContains = Node.prototype.contains || function(arg) {
2865           // jshint bitwise: false
2866           return !!(this.compareDocumentPosition(arg) & 16);
2867           // jshint bitwise: true
2868         };
2869
2870         /////////////////////////////////////////////
2871         function JQLite(element) {
2872           if (element instanceof JQLite) {
2873             return element;
2874           }
2875
2876           var argIsString;
2877
2878           if (isString(element)) {
2879             element = trim(element);
2880             argIsString = true;
2881           }
2882           if (!(this instanceof JQLite)) {
2883             if (argIsString && element.charAt(0) != '<') {
2884               throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2885             }
2886             return new JQLite(element);
2887           }
2888
2889           if (argIsString) {
2890             jqLiteAddNodes(this, jqLiteParseHTML(element));
2891           } else {
2892             jqLiteAddNodes(this, element);
2893           }
2894         }
2895
2896         function jqLiteClone(element) {
2897           return element.cloneNode(true);
2898         }
2899
2900         function jqLiteDealoc(element, onlyDescendants) {
2901           if (!onlyDescendants) jqLiteRemoveData(element);
2902
2903           if (element.querySelectorAll) {
2904             var descendants = element.querySelectorAll('*');
2905             for (var i = 0, l = descendants.length; i < l; i++) {
2906               jqLiteRemoveData(descendants[i]);
2907             }
2908           }
2909         }
2910
2911         function jqLiteOff(element, type, fn, unsupported) {
2912           if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2913
2914           var expandoStore = jqLiteExpandoStore(element);
2915           var events = expandoStore && expandoStore.events;
2916           var handle = expandoStore && expandoStore.handle;
2917
2918           if (!handle) return; //no listeners registered
2919
2920           if (!type) {
2921             for (type in events) {
2922               if (type !== '$destroy') {
2923                 removeEventListenerFn(element, type, handle);
2924               }
2925               delete events[type];
2926             }
2927           } else {
2928
2929             var removeHandler = function(type) {
2930               var listenerFns = events[type];
2931               if (isDefined(fn)) {
2932                 arrayRemove(listenerFns || [], fn);
2933               }
2934               if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2935                 removeEventListenerFn(element, type, handle);
2936                 delete events[type];
2937               }
2938             };
2939
2940             forEach(type.split(' '), function(type) {
2941               removeHandler(type);
2942               if (MOUSE_EVENT_MAP[type]) {
2943                 removeHandler(MOUSE_EVENT_MAP[type]);
2944               }
2945             });
2946           }
2947         }
2948
2949         function jqLiteRemoveData(element, name) {
2950           var expandoId = element.ng339;
2951           var expandoStore = expandoId && jqCache[expandoId];
2952
2953           if (expandoStore) {
2954             if (name) {
2955               delete expandoStore.data[name];
2956               return;
2957             }
2958
2959             if (expandoStore.handle) {
2960               if (expandoStore.events.$destroy) {
2961                 expandoStore.handle({}, '$destroy');
2962               }
2963               jqLiteOff(element);
2964             }
2965             delete jqCache[expandoId];
2966             element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2967           }
2968         }
2969
2970
2971         function jqLiteExpandoStore(element, createIfNecessary) {
2972           var expandoId = element.ng339,
2973               expandoStore = expandoId && jqCache[expandoId];
2974
2975           if (createIfNecessary && !expandoStore) {
2976             element.ng339 = expandoId = jqNextId();
2977             expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2978           }
2979
2980           return expandoStore;
2981         }
2982
2983
2984         function jqLiteData(element, key, value) {
2985           if (jqLiteAcceptsData(element)) {
2986
2987             var isSimpleSetter = isDefined(value);
2988             var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2989             var massGetter = !key;
2990             var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2991             var data = expandoStore && expandoStore.data;
2992
2993             if (isSimpleSetter) { // data('key', value)
2994               data[key] = value;
2995             } else {
2996               if (massGetter) {  // data()
2997                 return data;
2998               } else {
2999                 if (isSimpleGetter) { // data('key')
3000                   // don't force creation of expandoStore if it doesn't exist yet
3001                   return data && data[key];
3002                 } else { // mass-setter: data({key1: val1, key2: val2})
3003                   extend(data, key);
3004                 }
3005               }
3006             }
3007           }
3008         }
3009
3010         function jqLiteHasClass(element, selector) {
3011           if (!element.getAttribute) return false;
3012           return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3013               indexOf(" " + selector + " ") > -1);
3014         }
3015
3016         function jqLiteRemoveClass(element, cssClasses) {
3017           if (cssClasses && element.setAttribute) {
3018             forEach(cssClasses.split(' '), function(cssClass) {
3019               element.setAttribute('class', trim(
3020                   (" " + (element.getAttribute('class') || '') + " ")
3021                   .replace(/[\n\t]/g, " ")
3022                   .replace(" " + trim(cssClass) + " ", " "))
3023               );
3024             });
3025           }
3026         }
3027
3028         function jqLiteAddClass(element, cssClasses) {
3029           if (cssClasses && element.setAttribute) {
3030             var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3031                                     .replace(/[\n\t]/g, " ");
3032
3033             forEach(cssClasses.split(' '), function(cssClass) {
3034               cssClass = trim(cssClass);
3035               if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3036                 existingClasses += cssClass + ' ';
3037               }
3038             });
3039
3040             element.setAttribute('class', trim(existingClasses));
3041           }
3042         }
3043
3044
3045         function jqLiteAddNodes(root, elements) {
3046           // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3047
3048           if (elements) {
3049
3050             // if a Node (the most common case)
3051             if (elements.nodeType) {
3052               root[root.length++] = elements;
3053             } else {
3054               var length = elements.length;
3055
3056               // if an Array or NodeList and not a Window
3057               if (typeof length === 'number' && elements.window !== elements) {
3058                 if (length) {
3059                   for (var i = 0; i < length; i++) {
3060                     root[root.length++] = elements[i];
3061                   }
3062                 }
3063               } else {
3064                 root[root.length++] = elements;
3065               }
3066             }
3067           }
3068         }
3069
3070
3071         function jqLiteController(element, name) {
3072           return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3073         }
3074
3075         function jqLiteInheritedData(element, name, value) {
3076           // if element is the document object work with the html element instead
3077           // this makes $(document).scope() possible
3078           if (element.nodeType == NODE_TYPE_DOCUMENT) {
3079             element = element.documentElement;
3080           }
3081           var names = isArray(name) ? name : [name];
3082
3083           while (element) {
3084             for (var i = 0, ii = names.length; i < ii; i++) {
3085               if (isDefined(value = jqLite.data(element, names[i]))) return value;
3086             }
3087
3088             // If dealing with a document fragment node with a host element, and no parent, use the host
3089             // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3090             // to lookup parent controllers.
3091             element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3092           }
3093         }
3094
3095         function jqLiteEmpty(element) {
3096           jqLiteDealoc(element, true);
3097           while (element.firstChild) {
3098             element.removeChild(element.firstChild);
3099           }
3100         }
3101
3102         function jqLiteRemove(element, keepData) {
3103           if (!keepData) jqLiteDealoc(element);
3104           var parent = element.parentNode;
3105           if (parent) parent.removeChild(element);
3106         }
3107
3108
3109         function jqLiteDocumentLoaded(action, win) {
3110           win = win || window;
3111           if (win.document.readyState === 'complete') {
3112             // Force the action to be run async for consistent behaviour
3113             // from the action's point of view
3114             // i.e. it will definitely not be in a $apply
3115             win.setTimeout(action);
3116           } else {
3117             // No need to unbind this handler as load is only ever called once
3118             jqLite(win).on('load', action);
3119           }
3120         }
3121
3122         //////////////////////////////////////////
3123         // Functions which are declared directly.
3124         //////////////////////////////////////////
3125         var JQLitePrototype = JQLite.prototype = {
3126           ready: function(fn) {
3127             var fired = false;
3128
3129             function trigger() {
3130               if (fired) return;
3131               fired = true;
3132               fn();
3133             }
3134
3135             // check if document is already loaded
3136             if (document.readyState === 'complete') {
3137               setTimeout(trigger);
3138             } else {
3139               this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3140               // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3141               // jshint -W064
3142               JQLite(window).on('load', trigger); // fallback to window.onload for others
3143               // jshint +W064
3144             }
3145           },
3146           toString: function() {
3147             var value = [];
3148             forEach(this, function(e) { value.push('' + e);});
3149             return '[' + value.join(', ') + ']';
3150           },
3151
3152           eq: function(index) {
3153               return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3154           },
3155
3156           length: 0,
3157           push: push,
3158           sort: [].sort,
3159           splice: [].splice
3160         };
3161
3162         //////////////////////////////////////////
3163         // Functions iterating getter/setters.
3164         // these functions return self on setter and
3165         // value on get.
3166         //////////////////////////////////////////
3167         var BOOLEAN_ATTR = {};
3168         forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3169           BOOLEAN_ATTR[lowercase(value)] = value;
3170         });
3171         var BOOLEAN_ELEMENTS = {};
3172         forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3173           BOOLEAN_ELEMENTS[value] = true;
3174         });
3175         var ALIASED_ATTR = {
3176           'ngMinlength': 'minlength',
3177           'ngMaxlength': 'maxlength',
3178           'ngMin': 'min',
3179           'ngMax': 'max',
3180           'ngPattern': 'pattern'
3181         };
3182
3183         function getBooleanAttrName(element, name) {
3184           // check dom last since we will most likely fail on name
3185           var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3186
3187           // booleanAttr is here twice to minimize DOM access
3188           return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3189         }
3190
3191         function getAliasedAttrName(name) {
3192           return ALIASED_ATTR[name];
3193         }
3194
3195         forEach({
3196           data: jqLiteData,
3197           removeData: jqLiteRemoveData,
3198           hasData: jqLiteHasData
3199         }, function(fn, name) {
3200           JQLite[name] = fn;
3201         });
3202
3203         forEach({
3204           data: jqLiteData,
3205           inheritedData: jqLiteInheritedData,
3206
3207           scope: function(element) {
3208             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3209             return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3210           },
3211
3212           isolateScope: function(element) {
3213             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3214             return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3215           },
3216
3217           controller: jqLiteController,
3218
3219           injector: function(element) {
3220             return jqLiteInheritedData(element, '$injector');
3221           },
3222
3223           removeAttr: function(element, name) {
3224             element.removeAttribute(name);
3225           },
3226
3227           hasClass: jqLiteHasClass,
3228
3229           css: function(element, name, value) {
3230             name = camelCase(name);
3231
3232             if (isDefined(value)) {
3233               element.style[name] = value;
3234             } else {
3235               return element.style[name];
3236             }
3237           },
3238
3239           attr: function(element, name, value) {
3240             var nodeType = element.nodeType;
3241             if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3242               return;
3243             }
3244             var lowercasedName = lowercase(name);
3245             if (BOOLEAN_ATTR[lowercasedName]) {
3246               if (isDefined(value)) {
3247                 if (!!value) {
3248                   element[name] = true;
3249                   element.setAttribute(name, lowercasedName);
3250                 } else {
3251                   element[name] = false;
3252                   element.removeAttribute(lowercasedName);
3253                 }
3254               } else {
3255                 return (element[name] ||
3256                          (element.attributes.getNamedItem(name) || noop).specified)
3257                        ? lowercasedName
3258                        : undefined;
3259               }
3260             } else if (isDefined(value)) {
3261               element.setAttribute(name, value);
3262             } else if (element.getAttribute) {
3263               // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3264               // some elements (e.g. Document) don't have get attribute, so return undefined
3265               var ret = element.getAttribute(name, 2);
3266               // normalize non-existing attributes to undefined (as jQuery)
3267               return ret === null ? undefined : ret;
3268             }
3269           },
3270
3271           prop: function(element, name, value) {
3272             if (isDefined(value)) {
3273               element[name] = value;
3274             } else {
3275               return element[name];
3276             }
3277           },
3278
3279           text: (function() {
3280             getText.$dv = '';
3281             return getText;
3282
3283             function getText(element, value) {
3284               if (isUndefined(value)) {
3285                 var nodeType = element.nodeType;
3286                 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3287               }
3288               element.textContent = value;
3289             }
3290           })(),
3291
3292           val: function(element, value) {
3293             if (isUndefined(value)) {
3294               if (element.multiple && nodeName_(element) === 'select') {
3295                 var result = [];
3296                 forEach(element.options, function(option) {
3297                   if (option.selected) {
3298                     result.push(option.value || option.text);
3299                   }
3300                 });
3301                 return result.length === 0 ? null : result;
3302               }
3303               return element.value;
3304             }
3305             element.value = value;
3306           },
3307
3308           html: function(element, value) {
3309             if (isUndefined(value)) {
3310               return element.innerHTML;
3311             }
3312             jqLiteDealoc(element, true);
3313             element.innerHTML = value;
3314           },
3315
3316           empty: jqLiteEmpty
3317         }, function(fn, name) {
3318           /**
3319            * Properties: writes return selection, reads return first value
3320            */
3321           JQLite.prototype[name] = function(arg1, arg2) {
3322             var i, key;
3323             var nodeCount = this.length;
3324
3325             // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3326             // in a way that survives minification.
3327             // jqLiteEmpty takes no arguments but is a setter.
3328             if (fn !== jqLiteEmpty &&
3329                 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3330               if (isObject(arg1)) {
3331
3332                 // we are a write, but the object properties are the key/values
3333                 for (i = 0; i < nodeCount; i++) {
3334                   if (fn === jqLiteData) {
3335                     // data() takes the whole object in jQuery
3336                     fn(this[i], arg1);
3337                   } else {
3338                     for (key in arg1) {
3339                       fn(this[i], key, arg1[key]);
3340                     }
3341                   }
3342                 }
3343                 // return self for chaining
3344                 return this;
3345               } else {
3346                 // we are a read, so read the first child.
3347                 // TODO: do we still need this?
3348                 var value = fn.$dv;
3349                 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3350                 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3351                 for (var j = 0; j < jj; j++) {
3352                   var nodeValue = fn(this[j], arg1, arg2);
3353                   value = value ? value + nodeValue : nodeValue;
3354                 }
3355                 return value;
3356               }
3357             } else {
3358               // we are a write, so apply to all children
3359               for (i = 0; i < nodeCount; i++) {
3360                 fn(this[i], arg1, arg2);
3361               }
3362               // return self for chaining
3363               return this;
3364             }
3365           };
3366         });
3367
3368         function createEventHandler(element, events) {
3369           var eventHandler = function(event, type) {
3370             // jQuery specific api
3371             event.isDefaultPrevented = function() {
3372               return event.defaultPrevented;
3373             };
3374
3375             var eventFns = events[type || event.type];
3376             var eventFnsLength = eventFns ? eventFns.length : 0;
3377
3378             if (!eventFnsLength) return;
3379
3380             if (isUndefined(event.immediatePropagationStopped)) {
3381               var originalStopImmediatePropagation = event.stopImmediatePropagation;
3382               event.stopImmediatePropagation = function() {
3383                 event.immediatePropagationStopped = true;
3384
3385                 if (event.stopPropagation) {
3386                   event.stopPropagation();
3387                 }
3388
3389                 if (originalStopImmediatePropagation) {
3390                   originalStopImmediatePropagation.call(event);
3391                 }
3392               };
3393             }
3394
3395             event.isImmediatePropagationStopped = function() {
3396               return event.immediatePropagationStopped === true;
3397             };
3398
3399             // Some events have special handlers that wrap the real handler
3400             var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3401
3402             // Copy event handlers in case event handlers array is modified during execution.
3403             if ((eventFnsLength > 1)) {
3404               eventFns = shallowCopy(eventFns);
3405             }
3406
3407             for (var i = 0; i < eventFnsLength; i++) {
3408               if (!event.isImmediatePropagationStopped()) {
3409                 handlerWrapper(element, event, eventFns[i]);
3410               }
3411             }
3412           };
3413
3414           // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3415           //       events on `element`
3416           eventHandler.elem = element;
3417           return eventHandler;
3418         }
3419
3420         function defaultHandlerWrapper(element, event, handler) {
3421           handler.call(element, event);
3422         }
3423
3424         function specialMouseHandlerWrapper(target, event, handler) {
3425           // Refer to jQuery's implementation of mouseenter & mouseleave
3426           // Read about mouseenter and mouseleave:
3427           // http://www.quirksmode.org/js/events_mouse.html#link8
3428           var related = event.relatedTarget;
3429           // For mousenter/leave call the handler if related is outside the target.
3430           // NB: No relatedTarget if the mouse left/entered the browser window
3431           if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3432             handler.call(target, event);
3433           }
3434         }
3435
3436         //////////////////////////////////////////
3437         // Functions iterating traversal.
3438         // These functions chain results into a single
3439         // selector.
3440         //////////////////////////////////////////
3441         forEach({
3442           removeData: jqLiteRemoveData,
3443
3444           on: function jqLiteOn(element, type, fn, unsupported) {
3445             if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3446
3447             // Do not add event handlers to non-elements because they will not be cleaned up.
3448             if (!jqLiteAcceptsData(element)) {
3449               return;
3450             }
3451
3452             var expandoStore = jqLiteExpandoStore(element, true);
3453             var events = expandoStore.events;
3454             var handle = expandoStore.handle;
3455
3456             if (!handle) {
3457               handle = expandoStore.handle = createEventHandler(element, events);
3458             }
3459
3460             // http://jsperf.com/string-indexof-vs-split
3461             var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3462             var i = types.length;
3463
3464             var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3465               var eventFns = events[type];
3466
3467               if (!eventFns) {
3468                 eventFns = events[type] = [];
3469                 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3470                 if (type !== '$destroy' && !noEventListener) {
3471                   addEventListenerFn(element, type, handle);
3472                 }
3473               }
3474
3475               eventFns.push(fn);
3476             };
3477
3478             while (i--) {
3479               type = types[i];
3480               if (MOUSE_EVENT_MAP[type]) {
3481                 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3482                 addHandler(type, undefined, true);
3483               } else {
3484                 addHandler(type);
3485               }
3486             }
3487           },
3488
3489           off: jqLiteOff,
3490
3491           one: function(element, type, fn) {
3492             element = jqLite(element);
3493
3494             //add the listener twice so that when it is called
3495             //you can remove the original function and still be
3496             //able to call element.off(ev, fn) normally
3497             element.on(type, function onFn() {
3498               element.off(type, fn);
3499               element.off(type, onFn);
3500             });
3501             element.on(type, fn);
3502           },
3503
3504           replaceWith: function(element, replaceNode) {
3505             var index, parent = element.parentNode;
3506             jqLiteDealoc(element);
3507             forEach(new JQLite(replaceNode), function(node) {
3508               if (index) {
3509                 parent.insertBefore(node, index.nextSibling);
3510               } else {
3511                 parent.replaceChild(node, element);
3512               }
3513               index = node;
3514             });
3515           },
3516
3517           children: function(element) {
3518             var children = [];
3519             forEach(element.childNodes, function(element) {
3520               if (element.nodeType === NODE_TYPE_ELEMENT) {
3521                 children.push(element);
3522               }
3523             });
3524             return children;
3525           },
3526
3527           contents: function(element) {
3528             return element.contentDocument || element.childNodes || [];
3529           },
3530
3531           append: function(element, node) {
3532             var nodeType = element.nodeType;
3533             if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3534
3535             node = new JQLite(node);
3536
3537             for (var i = 0, ii = node.length; i < ii; i++) {
3538               var child = node[i];
3539               element.appendChild(child);
3540             }
3541           },
3542
3543           prepend: function(element, node) {
3544             if (element.nodeType === NODE_TYPE_ELEMENT) {
3545               var index = element.firstChild;
3546               forEach(new JQLite(node), function(child) {
3547                 element.insertBefore(child, index);
3548               });
3549             }
3550           },
3551
3552           wrap: function(element, wrapNode) {
3553             wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3554             var parent = element.parentNode;
3555             if (parent) {
3556               parent.replaceChild(wrapNode, element);
3557             }
3558             wrapNode.appendChild(element);
3559           },
3560
3561           remove: jqLiteRemove,
3562
3563           detach: function(element) {
3564             jqLiteRemove(element, true);
3565           },
3566
3567           after: function(element, newElement) {
3568             var index = element, parent = element.parentNode;
3569             newElement = new JQLite(newElement);
3570
3571             for (var i = 0, ii = newElement.length; i < ii; i++) {
3572               var node = newElement[i];
3573               parent.insertBefore(node, index.nextSibling);
3574               index = node;
3575             }
3576           },
3577
3578           addClass: jqLiteAddClass,
3579           removeClass: jqLiteRemoveClass,
3580
3581           toggleClass: function(element, selector, condition) {
3582             if (selector) {
3583               forEach(selector.split(' '), function(className) {
3584                 var classCondition = condition;
3585                 if (isUndefined(classCondition)) {
3586                   classCondition = !jqLiteHasClass(element, className);
3587                 }
3588                 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3589               });
3590             }
3591           },
3592
3593           parent: function(element) {
3594             var parent = element.parentNode;
3595             return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3596           },
3597
3598           next: function(element) {
3599             return element.nextElementSibling;
3600           },
3601
3602           find: function(element, selector) {
3603             if (element.getElementsByTagName) {
3604               return element.getElementsByTagName(selector);
3605             } else {
3606               return [];
3607             }
3608           },
3609
3610           clone: jqLiteClone,
3611
3612           triggerHandler: function(element, event, extraParameters) {
3613
3614             var dummyEvent, eventFnsCopy, handlerArgs;
3615             var eventName = event.type || event;
3616             var expandoStore = jqLiteExpandoStore(element);
3617             var events = expandoStore && expandoStore.events;
3618             var eventFns = events && events[eventName];
3619
3620             if (eventFns) {
3621               // Create a dummy event to pass to the handlers
3622               dummyEvent = {
3623                 preventDefault: function() { this.defaultPrevented = true; },
3624                 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3625                 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3626                 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3627                 stopPropagation: noop,
3628                 type: eventName,
3629                 target: element
3630               };
3631
3632               // If a custom event was provided then extend our dummy event with it
3633               if (event.type) {
3634                 dummyEvent = extend(dummyEvent, event);
3635               }
3636
3637               // Copy event handlers in case event handlers array is modified during execution.
3638               eventFnsCopy = shallowCopy(eventFns);
3639               handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3640
3641               forEach(eventFnsCopy, function(fn) {
3642                 if (!dummyEvent.isImmediatePropagationStopped()) {
3643                   fn.apply(element, handlerArgs);
3644                 }
3645               });
3646             }
3647           }
3648         }, function(fn, name) {
3649           /**
3650            * chaining functions
3651            */
3652           JQLite.prototype[name] = function(arg1, arg2, arg3) {
3653             var value;
3654
3655             for (var i = 0, ii = this.length; i < ii; i++) {
3656               if (isUndefined(value)) {
3657                 value = fn(this[i], arg1, arg2, arg3);
3658                 if (isDefined(value)) {
3659                   // any function which returns a value needs to be wrapped
3660                   value = jqLite(value);
3661                 }
3662               } else {
3663                 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3664               }
3665             }
3666             return isDefined(value) ? value : this;
3667           };
3668
3669           // bind legacy bind/unbind to on/off
3670           JQLite.prototype.bind = JQLite.prototype.on;
3671           JQLite.prototype.unbind = JQLite.prototype.off;
3672         });
3673
3674
3675         // Provider for private $$jqLite service
3676         function $$jqLiteProvider() {
3677           this.$get = function $$jqLite() {
3678             return extend(JQLite, {
3679               hasClass: function(node, classes) {
3680                 if (node.attr) node = node[0];
3681                 return jqLiteHasClass(node, classes);
3682               },
3683               addClass: function(node, classes) {
3684                 if (node.attr) node = node[0];
3685                 return jqLiteAddClass(node, classes);
3686               },
3687               removeClass: function(node, classes) {
3688                 if (node.attr) node = node[0];
3689                 return jqLiteRemoveClass(node, classes);
3690               }
3691             });
3692           };
3693         }
3694
3695         /**
3696          * Computes a hash of an 'obj'.
3697          * Hash of a:
3698          *  string is string
3699          *  number is number as string
3700          *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3701          *         that is also assigned to the $$hashKey property of the object.
3702          *
3703          * @param obj
3704          * @returns {string} hash string such that the same input will have the same hash string.
3705          *         The resulting string key is in 'type:hashKey' format.
3706          */
3707         function hashKey(obj, nextUidFn) {
3708           var key = obj && obj.$$hashKey;
3709
3710           if (key) {
3711             if (typeof key === 'function') {
3712               key = obj.$$hashKey();
3713             }
3714             return key;
3715           }
3716
3717           var objType = typeof obj;
3718           if (objType == 'function' || (objType == 'object' && obj !== null)) {
3719             key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3720           } else {
3721             key = objType + ':' + obj;
3722           }
3723
3724           return key;
3725         }
3726
3727         /**
3728          * HashMap which can use objects as keys
3729          */
3730         function HashMap(array, isolatedUid) {
3731           if (isolatedUid) {
3732             var uid = 0;
3733             this.nextUid = function() {
3734               return ++uid;
3735             };
3736           }
3737           forEach(array, this.put, this);
3738         }
3739         HashMap.prototype = {
3740           /**
3741            * Store key value pair
3742            * @param key key to store can be any type
3743            * @param value value to store can be any type
3744            */
3745           put: function(key, value) {
3746             this[hashKey(key, this.nextUid)] = value;
3747           },
3748
3749           /**
3750            * @param key
3751            * @returns {Object} the value for the key
3752            */
3753           get: function(key) {
3754             return this[hashKey(key, this.nextUid)];
3755           },
3756
3757           /**
3758            * Remove the key/value pair
3759            * @param key
3760            */
3761           remove: function(key) {
3762             var value = this[key = hashKey(key, this.nextUid)];
3763             delete this[key];
3764             return value;
3765           }
3766         };
3767
3768         var $$HashMapProvider = [function() {
3769           this.$get = [function() {
3770             return HashMap;
3771           }];
3772         }];
3773
3774         /**
3775          * @ngdoc function
3776          * @module ng
3777          * @name angular.injector
3778          * @kind function
3779          *
3780          * @description
3781          * Creates an injector object that can be used for retrieving services as well as for
3782          * dependency injection (see {@link guide/di dependency injection}).
3783          *
3784          * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3785          *     {@link angular.module}. The `ng` module must be explicitly added.
3786          * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3787          *     disallows argument name annotation inference.
3788          * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3789          *
3790          * @example
3791          * Typical usage
3792          * ```js
3793          *   // create an injector
3794          *   var $injector = angular.injector(['ng']);
3795          *
3796          *   // use the injector to kick off your application
3797          *   // use the type inference to auto inject arguments, or use implicit injection
3798          *   $injector.invoke(function($rootScope, $compile, $document) {
3799          *     $compile($document)($rootScope);
3800          *     $rootScope.$digest();
3801          *   });
3802          * ```
3803          *
3804          * Sometimes you want to get access to the injector of a currently running Angular app
3805          * from outside Angular. Perhaps, you want to inject and compile some markup after the
3806          * application has been bootstrapped. You can do this using the extra `injector()` added
3807          * to JQuery/jqLite elements. See {@link angular.element}.
3808          *
3809          * *This is fairly rare but could be the case if a third party library is injecting the
3810          * markup.*
3811          *
3812          * In the following example a new block of HTML containing a `ng-controller`
3813          * directive is added to the end of the document body by JQuery. We then compile and link
3814          * it into the current AngularJS scope.
3815          *
3816          * ```js
3817          * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3818          * $(document.body).append($div);
3819          *
3820          * angular.element(document).injector().invoke(function($compile) {
3821          *   var scope = angular.element($div).scope();
3822          *   $compile($div)(scope);
3823          * });
3824          * ```
3825          */
3826
3827
3828         /**
3829          * @ngdoc module
3830          * @name auto
3831          * @description
3832          *
3833          * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3834          */
3835
3836         var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3837         var FN_ARG_SPLIT = /,/;
3838         var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3839         var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3840         var $injectorMinErr = minErr('$injector');
3841
3842         function anonFn(fn) {
3843           // For anonymous functions, showing at the very least the function signature can help in
3844           // debugging.
3845           var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3846               args = fnText.match(FN_ARGS);
3847           if (args) {
3848             return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3849           }
3850           return 'fn';
3851         }
3852
3853         function annotate(fn, strictDi, name) {
3854           var $inject,
3855               fnText,
3856               argDecl,
3857               last;
3858
3859           if (typeof fn === 'function') {
3860             if (!($inject = fn.$inject)) {
3861               $inject = [];
3862               if (fn.length) {
3863                 if (strictDi) {
3864                   if (!isString(name) || !name) {
3865                     name = fn.name || anonFn(fn);
3866                   }
3867                   throw $injectorMinErr('strictdi',
3868                     '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3869                 }
3870                 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3871                 argDecl = fnText.match(FN_ARGS);
3872                 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3873                   arg.replace(FN_ARG, function(all, underscore, name) {
3874                     $inject.push(name);
3875                   });
3876                 });
3877               }
3878               fn.$inject = $inject;
3879             }
3880           } else if (isArray(fn)) {
3881             last = fn.length - 1;
3882             assertArgFn(fn[last], 'fn');
3883             $inject = fn.slice(0, last);
3884           } else {
3885             assertArgFn(fn, 'fn', true);
3886           }
3887           return $inject;
3888         }
3889
3890         ///////////////////////////////////////
3891
3892         /**
3893          * @ngdoc service
3894          * @name $injector
3895          *
3896          * @description
3897          *
3898          * `$injector` is used to retrieve object instances as defined by
3899          * {@link auto.$provide provider}, instantiate types, invoke methods,
3900          * and load modules.
3901          *
3902          * The following always holds true:
3903          *
3904          * ```js
3905          *   var $injector = angular.injector();
3906          *   expect($injector.get('$injector')).toBe($injector);
3907          *   expect($injector.invoke(function($injector) {
3908          *     return $injector;
3909          *   })).toBe($injector);
3910          * ```
3911          *
3912          * # Injection Function Annotation
3913          *
3914          * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3915          * following are all valid ways of annotating function with injection arguments and are equivalent.
3916          *
3917          * ```js
3918          *   // inferred (only works if code not minified/obfuscated)
3919          *   $injector.invoke(function(serviceA){});
3920          *
3921          *   // annotated
3922          *   function explicit(serviceA) {};
3923          *   explicit.$inject = ['serviceA'];
3924          *   $injector.invoke(explicit);
3925          *
3926          *   // inline
3927          *   $injector.invoke(['serviceA', function(serviceA){}]);
3928          * ```
3929          *
3930          * ## Inference
3931          *
3932          * In JavaScript calling `toString()` on a function returns the function definition. The definition
3933          * can then be parsed and the function arguments can be extracted. This method of discovering
3934          * annotations is disallowed when the injector is in strict mode.
3935          * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3936          * argument names.
3937          *
3938          * ## `$inject` Annotation
3939          * By adding an `$inject` property onto a function the injection parameters can be specified.
3940          *
3941          * ## Inline
3942          * As an array of injection names, where the last item in the array is the function to call.
3943          */
3944
3945         /**
3946          * @ngdoc method
3947          * @name $injector#get
3948          *
3949          * @description
3950          * Return an instance of the service.
3951          *
3952          * @param {string} name The name of the instance to retrieve.
3953          * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3954          * @return {*} The instance.
3955          */
3956
3957         /**
3958          * @ngdoc method
3959          * @name $injector#invoke
3960          *
3961          * @description
3962          * Invoke the method and supply the method arguments from the `$injector`.
3963          *
3964          * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3965          *   injected according to the {@link guide/di $inject Annotation} rules.
3966          * @param {Object=} self The `this` for the invoked method.
3967          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3968          *                         object first, before the `$injector` is consulted.
3969          * @returns {*} the value returned by the invoked `fn` function.
3970          */
3971
3972         /**
3973          * @ngdoc method
3974          * @name $injector#has
3975          *
3976          * @description
3977          * Allows the user to query if the particular service exists.
3978          *
3979          * @param {string} name Name of the service to query.
3980          * @returns {boolean} `true` if injector has given service.
3981          */
3982
3983         /**
3984          * @ngdoc method
3985          * @name $injector#instantiate
3986          * @description
3987          * Create a new instance of JS type. The method takes a constructor function, invokes the new
3988          * operator, and supplies all of the arguments to the constructor function as specified by the
3989          * constructor annotation.
3990          *
3991          * @param {Function} Type Annotated constructor function.
3992          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3993          * object first, before the `$injector` is consulted.
3994          * @returns {Object} new instance of `Type`.
3995          */
3996
3997         /**
3998          * @ngdoc method
3999          * @name $injector#annotate
4000          *
4001          * @description
4002          * Returns an array of service names which the function is requesting for injection. This API is
4003          * used by the injector to determine which services need to be injected into the function when the
4004          * function is invoked. There are three ways in which the function can be annotated with the needed
4005          * dependencies.
4006          *
4007          * # Argument names
4008          *
4009          * The simplest form is to extract the dependencies from the arguments of the function. This is done
4010          * by converting the function into a string using `toString()` method and extracting the argument
4011          * names.
4012          * ```js
4013          *   // Given
4014          *   function MyController($scope, $route) {
4015          *     // ...
4016          *   }
4017          *
4018          *   // Then
4019          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4020          * ```
4021          *
4022          * You can disallow this method by using strict injection mode.
4023          *
4024          * This method does not work with code minification / obfuscation. For this reason the following
4025          * annotation strategies are supported.
4026          *
4027          * # The `$inject` property
4028          *
4029          * If a function has an `$inject` property and its value is an array of strings, then the strings
4030          * represent names of services to be injected into the function.
4031          * ```js
4032          *   // Given
4033          *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4034          *     // ...
4035          *   }
4036          *   // Define function dependencies
4037          *   MyController['$inject'] = ['$scope', '$route'];
4038          *
4039          *   // Then
4040          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4041          * ```
4042          *
4043          * # The array notation
4044          *
4045          * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4046          * is very inconvenient. In these situations using the array notation to specify the dependencies in
4047          * a way that survives minification is a better choice:
4048          *
4049          * ```js
4050          *   // We wish to write this (not minification / obfuscation safe)
4051          *   injector.invoke(function($compile, $rootScope) {
4052          *     // ...
4053          *   });
4054          *
4055          *   // We are forced to write break inlining
4056          *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4057          *     // ...
4058          *   };
4059          *   tmpFn.$inject = ['$compile', '$rootScope'];
4060          *   injector.invoke(tmpFn);
4061          *
4062          *   // To better support inline function the inline annotation is supported
4063          *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4064          *     // ...
4065          *   }]);
4066          *
4067          *   // Therefore
4068          *   expect(injector.annotate(
4069          *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4070          *    ).toEqual(['$compile', '$rootScope']);
4071          * ```
4072          *
4073          * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4074          * be retrieved as described above.
4075          *
4076          * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4077          *
4078          * @returns {Array.<string>} The names of the services which the function requires.
4079          */
4080
4081
4082
4083
4084         /**
4085          * @ngdoc service
4086          * @name $provide
4087          *
4088          * @description
4089          *
4090          * The {@link auto.$provide $provide} service has a number of methods for registering components
4091          * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4092          * {@link angular.Module}.
4093          *
4094          * An Angular **service** is a singleton object created by a **service factory**.  These **service
4095          * factories** are functions which, in turn, are created by a **service provider**.
4096          * The **service providers** are constructor functions. When instantiated they must contain a
4097          * property called `$get`, which holds the **service factory** function.
4098          *
4099          * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4100          * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4101          * function to get the instance of the **service**.
4102          *
4103          * Often services have no configuration options and there is no need to add methods to the service
4104          * provider.  The provider will be no more than a constructor function with a `$get` property. For
4105          * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4106          * services without specifying a provider.
4107          *
4108          * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4109          *     {@link auto.$injector $injector}
4110          * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4111          *     providers and services.
4112          * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4113          *     services, not providers.
4114          * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4115          *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4116          *     given factory function.
4117          * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4118          *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4119          *      a new object using the given constructor function.
4120          *
4121          * See the individual methods for more information and examples.
4122          */
4123
4124         /**
4125          * @ngdoc method
4126          * @name $provide#provider
4127          * @description
4128          *
4129          * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4130          * are constructor functions, whose instances are responsible for "providing" a factory for a
4131          * service.
4132          *
4133          * Service provider names start with the name of the service they provide followed by `Provider`.
4134          * For example, the {@link ng.$log $log} service has a provider called
4135          * {@link ng.$logProvider $logProvider}.
4136          *
4137          * Service provider objects can have additional methods which allow configuration of the provider
4138          * and its service. Importantly, you can configure what kind of service is created by the `$get`
4139          * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4140          * method {@link ng.$logProvider#debugEnabled debugEnabled}
4141          * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4142          * console or not.
4143          *
4144          * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4145                                 'Provider'` key.
4146          * @param {(Object|function())} provider If the provider is:
4147          *
4148          *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4149          *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4150          *   - `Constructor`: a new instance of the provider will be created using
4151          *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4152          *
4153          * @returns {Object} registered provider instance
4154
4155          * @example
4156          *
4157          * The following example shows how to create a simple event tracking service and register it using
4158          * {@link auto.$provide#provider $provide.provider()}.
4159          *
4160          * ```js
4161          *  // Define the eventTracker provider
4162          *  function EventTrackerProvider() {
4163          *    var trackingUrl = '/track';
4164          *
4165          *    // A provider method for configuring where the tracked events should been saved
4166          *    this.setTrackingUrl = function(url) {
4167          *      trackingUrl = url;
4168          *    };
4169          *
4170          *    // The service factory function
4171          *    this.$get = ['$http', function($http) {
4172          *      var trackedEvents = {};
4173          *      return {
4174          *        // Call this to track an event
4175          *        event: function(event) {
4176          *          var count = trackedEvents[event] || 0;
4177          *          count += 1;
4178          *          trackedEvents[event] = count;
4179          *          return count;
4180          *        },
4181          *        // Call this to save the tracked events to the trackingUrl
4182          *        save: function() {
4183          *          $http.post(trackingUrl, trackedEvents);
4184          *        }
4185          *      };
4186          *    }];
4187          *  }
4188          *
4189          *  describe('eventTracker', function() {
4190          *    var postSpy;
4191          *
4192          *    beforeEach(module(function($provide) {
4193          *      // Register the eventTracker provider
4194          *      $provide.provider('eventTracker', EventTrackerProvider);
4195          *    }));
4196          *
4197          *    beforeEach(module(function(eventTrackerProvider) {
4198          *      // Configure eventTracker provider
4199          *      eventTrackerProvider.setTrackingUrl('/custom-track');
4200          *    }));
4201          *
4202          *    it('tracks events', inject(function(eventTracker) {
4203          *      expect(eventTracker.event('login')).toEqual(1);
4204          *      expect(eventTracker.event('login')).toEqual(2);
4205          *    }));
4206          *
4207          *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4208          *      postSpy = spyOn($http, 'post');
4209          *      eventTracker.event('login');
4210          *      eventTracker.save();
4211          *      expect(postSpy).toHaveBeenCalled();
4212          *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4213          *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4214          *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4215          *    }));
4216          *  });
4217          * ```
4218          */
4219
4220         /**
4221          * @ngdoc method
4222          * @name $provide#factory
4223          * @description
4224          *
4225          * Register a **service factory**, which will be called to return the service instance.
4226          * This is short for registering a service where its provider consists of only a `$get` property,
4227          * which is the given service factory function.
4228          * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4229          * configure your service in a provider.
4230          *
4231          * @param {string} name The name of the instance.
4232          * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4233          *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4234          * @returns {Object} registered provider instance
4235          *
4236          * @example
4237          * Here is an example of registering a service
4238          * ```js
4239          *   $provide.factory('ping', ['$http', function($http) {
4240          *     return function ping() {
4241          *       return $http.send('/ping');
4242          *     };
4243          *   }]);
4244          * ```
4245          * You would then inject and use this service like this:
4246          * ```js
4247          *   someModule.controller('Ctrl', ['ping', function(ping) {
4248          *     ping();
4249          *   }]);
4250          * ```
4251          */
4252
4253
4254         /**
4255          * @ngdoc method
4256          * @name $provide#service
4257          * @description
4258          *
4259          * Register a **service constructor**, which will be invoked with `new` to create the service
4260          * instance.
4261          * This is short for registering a service where its provider's `$get` property is the service
4262          * constructor function that will be used to instantiate the service instance.
4263          *
4264          * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4265          * as a type/class.
4266          *
4267          * @param {string} name The name of the instance.
4268          * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4269          *     that will be instantiated.
4270          * @returns {Object} registered provider instance
4271          *
4272          * @example
4273          * Here is an example of registering a service using
4274          * {@link auto.$provide#service $provide.service(class)}.
4275          * ```js
4276          *   var Ping = function($http) {
4277          *     this.$http = $http;
4278          *   };
4279          *
4280          *   Ping.$inject = ['$http'];
4281          *
4282          *   Ping.prototype.send = function() {
4283          *     return this.$http.get('/ping');
4284          *   };
4285          *   $provide.service('ping', Ping);
4286          * ```
4287          * You would then inject and use this service like this:
4288          * ```js
4289          *   someModule.controller('Ctrl', ['ping', function(ping) {
4290          *     ping.send();
4291          *   }]);
4292          * ```
4293          */
4294
4295
4296         /**
4297          * @ngdoc method
4298          * @name $provide#value
4299          * @description
4300          *
4301          * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4302          * number, an array, an object or a function.  This is short for registering a service where its
4303          * provider's `$get` property is a factory function that takes no arguments and returns the **value
4304          * service**.
4305          *
4306          * Value services are similar to constant services, except that they cannot be injected into a
4307          * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4308          * an Angular
4309          * {@link auto.$provide#decorator decorator}.
4310          *
4311          * @param {string} name The name of the instance.
4312          * @param {*} value The value.
4313          * @returns {Object} registered provider instance
4314          *
4315          * @example
4316          * Here are some examples of creating value services.
4317          * ```js
4318          *   $provide.value('ADMIN_USER', 'admin');
4319          *
4320          *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4321          *
4322          *   $provide.value('halfOf', function(value) {
4323          *     return value / 2;
4324          *   });
4325          * ```
4326          */
4327
4328
4329         /**
4330          * @ngdoc method
4331          * @name $provide#constant
4332          * @description
4333          *
4334          * Register a **constant service**, such as a string, a number, an array, an object or a function,
4335          * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4336          * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4337          * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4338          *
4339          * @param {string} name The name of the constant.
4340          * @param {*} value The constant value.
4341          * @returns {Object} registered instance
4342          *
4343          * @example
4344          * Here a some examples of creating constants:
4345          * ```js
4346          *   $provide.constant('SHARD_HEIGHT', 306);
4347          *
4348          *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4349          *
4350          *   $provide.constant('double', function(value) {
4351          *     return value * 2;
4352          *   });
4353          * ```
4354          */
4355
4356
4357         /**
4358          * @ngdoc method
4359          * @name $provide#decorator
4360          * @description
4361          *
4362          * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4363          * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4364          * service. The object returned by the decorator may be the original service, or a new service
4365          * object which replaces or wraps and delegates to the original service.
4366          *
4367          * @param {string} name The name of the service to decorate.
4368          * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4369          *    instantiated and should return the decorated service instance. The function is called using
4370          *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4371          *    Local injection arguments:
4372          *
4373          *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4374          *      decorated or delegated to.
4375          *
4376          * @example
4377          * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4378          * calls to {@link ng.$log#error $log.warn()}.
4379          * ```js
4380          *   $provide.decorator('$log', ['$delegate', function($delegate) {
4381          *     $delegate.warn = $delegate.error;
4382          *     return $delegate;
4383          *   }]);
4384          * ```
4385          */
4386
4387
4388         function createInjector(modulesToLoad, strictDi) {
4389           strictDi = (strictDi === true);
4390           var INSTANTIATING = {},
4391               providerSuffix = 'Provider',
4392               path = [],
4393               loadedModules = new HashMap([], true),
4394               providerCache = {
4395                 $provide: {
4396                     provider: supportObject(provider),
4397                     factory: supportObject(factory),
4398                     service: supportObject(service),
4399                     value: supportObject(value),
4400                     constant: supportObject(constant),
4401                     decorator: decorator
4402                   }
4403               },
4404               providerInjector = (providerCache.$injector =
4405                   createInternalInjector(providerCache, function(serviceName, caller) {
4406                     if (angular.isString(caller)) {
4407                       path.push(caller);
4408                     }
4409                     throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4410                   })),
4411               instanceCache = {},
4412               instanceInjector = (instanceCache.$injector =
4413                   createInternalInjector(instanceCache, function(serviceName, caller) {
4414                     var provider = providerInjector.get(serviceName + providerSuffix, caller);
4415                     return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4416                   }));
4417
4418
4419           forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4420
4421           return instanceInjector;
4422
4423           ////////////////////////////////////
4424           // $provider
4425           ////////////////////////////////////
4426
4427           function supportObject(delegate) {
4428             return function(key, value) {
4429               if (isObject(key)) {
4430                 forEach(key, reverseParams(delegate));
4431               } else {
4432                 return delegate(key, value);
4433               }
4434             };
4435           }
4436
4437           function provider(name, provider_) {
4438             assertNotHasOwnProperty(name, 'service');
4439             if (isFunction(provider_) || isArray(provider_)) {
4440               provider_ = providerInjector.instantiate(provider_);
4441             }
4442             if (!provider_.$get) {
4443               throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4444             }
4445             return providerCache[name + providerSuffix] = provider_;
4446           }
4447
4448           function enforceReturnValue(name, factory) {
4449             return function enforcedReturnValue() {
4450               var result = instanceInjector.invoke(factory, this);
4451               if (isUndefined(result)) {
4452                 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4453               }
4454               return result;
4455             };
4456           }
4457
4458           function factory(name, factoryFn, enforce) {
4459             return provider(name, {
4460               $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4461             });
4462           }
4463
4464           function service(name, constructor) {
4465             return factory(name, ['$injector', function($injector) {
4466               return $injector.instantiate(constructor);
4467             }]);
4468           }
4469
4470           function value(name, val) { return factory(name, valueFn(val), false); }
4471
4472           function constant(name, value) {
4473             assertNotHasOwnProperty(name, 'constant');
4474             providerCache[name] = value;
4475             instanceCache[name] = value;
4476           }
4477
4478           function decorator(serviceName, decorFn) {
4479             var origProvider = providerInjector.get(serviceName + providerSuffix),
4480                 orig$get = origProvider.$get;
4481
4482             origProvider.$get = function() {
4483               var origInstance = instanceInjector.invoke(orig$get, origProvider);
4484               return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4485             };
4486           }
4487
4488           ////////////////////////////////////
4489           // Module Loading
4490           ////////////////////////////////////
4491           function loadModules(modulesToLoad) {
4492             assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4493             var runBlocks = [], moduleFn;
4494             forEach(modulesToLoad, function(module) {
4495               if (loadedModules.get(module)) return;
4496               loadedModules.put(module, true);
4497
4498               function runInvokeQueue(queue) {
4499                 var i, ii;
4500                 for (i = 0, ii = queue.length; i < ii; i++) {
4501                   var invokeArgs = queue[i],
4502                       provider = providerInjector.get(invokeArgs[0]);
4503
4504                   provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4505                 }
4506               }
4507
4508               try {
4509                 if (isString(module)) {
4510                   moduleFn = angularModule(module);
4511                   runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4512                   runInvokeQueue(moduleFn._invokeQueue);
4513                   runInvokeQueue(moduleFn._configBlocks);
4514                 } else if (isFunction(module)) {
4515                     runBlocks.push(providerInjector.invoke(module));
4516                 } else if (isArray(module)) {
4517                     runBlocks.push(providerInjector.invoke(module));
4518                 } else {
4519                   assertArgFn(module, 'module');
4520                 }
4521               } catch (e) {
4522                 if (isArray(module)) {
4523                   module = module[module.length - 1];
4524                 }
4525                 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4526                   // Safari & FF's stack traces don't contain error.message content
4527                   // unlike those of Chrome and IE
4528                   // So if stack doesn't contain message, we create a new string that contains both.
4529                   // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4530                   /* jshint -W022 */
4531                   e = e.message + '\n' + e.stack;
4532                 }
4533                 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4534                           module, e.stack || e.message || e);
4535               }
4536             });
4537             return runBlocks;
4538           }
4539
4540           ////////////////////////////////////
4541           // internal Injector
4542           ////////////////////////////////////
4543
4544           function createInternalInjector(cache, factory) {
4545
4546             function getService(serviceName, caller) {
4547               if (cache.hasOwnProperty(serviceName)) {
4548                 if (cache[serviceName] === INSTANTIATING) {
4549                   throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4550                             serviceName + ' <- ' + path.join(' <- '));
4551                 }
4552                 return cache[serviceName];
4553               } else {
4554                 try {
4555                   path.unshift(serviceName);
4556                   cache[serviceName] = INSTANTIATING;
4557                   return cache[serviceName] = factory(serviceName, caller);
4558                 } catch (err) {
4559                   if (cache[serviceName] === INSTANTIATING) {
4560                     delete cache[serviceName];
4561                   }
4562                   throw err;
4563                 } finally {
4564                   path.shift();
4565                 }
4566               }
4567             }
4568
4569             function invoke(fn, self, locals, serviceName) {
4570               if (typeof locals === 'string') {
4571                 serviceName = locals;
4572                 locals = null;
4573               }
4574
4575               var args = [],
4576                   $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4577                   length, i,
4578                   key;
4579
4580               for (i = 0, length = $inject.length; i < length; i++) {
4581                 key = $inject[i];
4582                 if (typeof key !== 'string') {
4583                   throw $injectorMinErr('itkn',
4584                           'Incorrect injection token! Expected service name as string, got {0}', key);
4585                 }
4586                 args.push(
4587                   locals && locals.hasOwnProperty(key)
4588                   ? locals[key]
4589                   : getService(key, serviceName)
4590                 );
4591               }
4592               if (isArray(fn)) {
4593                 fn = fn[length];
4594               }
4595
4596               // http://jsperf.com/angularjs-invoke-apply-vs-switch
4597               // #5388
4598               return fn.apply(self, args);
4599             }
4600
4601             function instantiate(Type, locals, serviceName) {
4602               // Check if Type is annotated and use just the given function at n-1 as parameter
4603               // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4604               // Object creation: http://jsperf.com/create-constructor/2
4605               var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4606               var returnedValue = invoke(Type, instance, locals, serviceName);
4607
4608               return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4609             }
4610
4611             return {
4612               invoke: invoke,
4613               instantiate: instantiate,
4614               get: getService,
4615               annotate: createInjector.$$annotate,
4616               has: function(name) {
4617                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4618               }
4619             };
4620           }
4621         }
4622
4623         createInjector.$$annotate = annotate;
4624
4625         /**
4626          * @ngdoc provider
4627          * @name $anchorScrollProvider
4628          *
4629          * @description
4630          * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4631          * {@link ng.$location#hash $location.hash()} changes.
4632          */
4633         function $AnchorScrollProvider() {
4634
4635           var autoScrollingEnabled = true;
4636
4637           /**
4638            * @ngdoc method
4639            * @name $anchorScrollProvider#disableAutoScrolling
4640            *
4641            * @description
4642            * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4643            * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4644            * Use this method to disable automatic scrolling.
4645            *
4646            * If automatic scrolling is disabled, one must explicitly call
4647            * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4648            * current hash.
4649            */
4650           this.disableAutoScrolling = function() {
4651             autoScrollingEnabled = false;
4652           };
4653
4654           /**
4655            * @ngdoc service
4656            * @name $anchorScroll
4657            * @kind function
4658            * @requires $window
4659            * @requires $location
4660            * @requires $rootScope
4661            *
4662            * @description
4663            * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4664            * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4665            * in the
4666            * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4667            *
4668            * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4669            * match any anchor whenever it changes. This can be disabled by calling
4670            * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4671            *
4672            * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4673            * vertical scroll-offset (either fixed or dynamic).
4674            *
4675            * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4676            *                       {@link ng.$location#hash $location.hash()} will be used.
4677            *
4678            * @property {(number|function|jqLite)} yOffset
4679            * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4680            * positioned elements at the top of the page, such as navbars, headers etc.
4681            *
4682            * `yOffset` can be specified in various ways:
4683            * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4684            * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4685            *   a number representing the offset (in pixels).<br /><br />
4686            * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4687            *   the top of the page to the element's bottom will be used as offset.<br />
4688            *   **Note**: The element will be taken into account only as long as its `position` is set to
4689            *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4690            *   their height and/or positioning according to the viewport's size.
4691            *
4692            * <br />
4693            * <div class="alert alert-warning">
4694            * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4695            * not some child element.
4696            * </div>
4697            *
4698            * @example
4699              <example module="anchorScrollExample">
4700                <file name="index.html">
4701                  <div id="scrollArea" ng-controller="ScrollController">
4702                    <a ng-click="gotoBottom()">Go to bottom</a>
4703                    <a id="bottom"></a> You're at the bottom!
4704                  </div>
4705                </file>
4706                <file name="script.js">
4707                  angular.module('anchorScrollExample', [])
4708                    .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4709                      function ($scope, $location, $anchorScroll) {
4710                        $scope.gotoBottom = function() {
4711                          // set the location.hash to the id of
4712                          // the element you wish to scroll to.
4713                          $location.hash('bottom');
4714
4715                          // call $anchorScroll()
4716                          $anchorScroll();
4717                        };
4718                      }]);
4719                </file>
4720                <file name="style.css">
4721                  #scrollArea {
4722                    height: 280px;
4723                    overflow: auto;
4724                  }
4725
4726                  #bottom {
4727                    display: block;
4728                    margin-top: 2000px;
4729                  }
4730                </file>
4731              </example>
4732            *
4733            * <hr />
4734            * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4735            * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4736            *
4737            * @example
4738              <example module="anchorScrollOffsetExample">
4739                <file name="index.html">
4740                  <div class="fixed-header" ng-controller="headerCtrl">
4741                    <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4742                      Go to anchor {{x}}
4743                    </a>
4744                  </div>
4745                  <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4746                    Anchor {{x}} of 5
4747                  </div>
4748                </file>
4749                <file name="script.js">
4750                  angular.module('anchorScrollOffsetExample', [])
4751                    .run(['$anchorScroll', function($anchorScroll) {
4752                      $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4753                    }])
4754                    .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4755                      function ($anchorScroll, $location, $scope) {
4756                        $scope.gotoAnchor = function(x) {
4757                          var newHash = 'anchor' + x;
4758                          if ($location.hash() !== newHash) {
4759                            // set the $location.hash to `newHash` and
4760                            // $anchorScroll will automatically scroll to it
4761                            $location.hash('anchor' + x);
4762                          } else {
4763                            // call $anchorScroll() explicitly,
4764                            // since $location.hash hasn't changed
4765                            $anchorScroll();
4766                          }
4767                        };
4768                      }
4769                    ]);
4770                </file>
4771                <file name="style.css">
4772                  body {
4773                    padding-top: 50px;
4774                  }
4775
4776                  .anchor {
4777                    border: 2px dashed DarkOrchid;
4778                    padding: 10px 10px 200px 10px;
4779                  }
4780
4781                  .fixed-header {
4782                    background-color: rgba(0, 0, 0, 0.2);
4783                    height: 50px;
4784                    position: fixed;
4785                    top: 0; left: 0; right: 0;
4786                  }
4787
4788                  .fixed-header > a {
4789                    display: inline-block;
4790                    margin: 5px 15px;
4791                  }
4792                </file>
4793              </example>
4794            */
4795           this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4796             var document = $window.document;
4797
4798             // Helper function to get first anchor from a NodeList
4799             // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4800             //  and working in all supported browsers.)
4801             function getFirstAnchor(list) {
4802               var result = null;
4803               Array.prototype.some.call(list, function(element) {
4804                 if (nodeName_(element) === 'a') {
4805                   result = element;
4806                   return true;
4807                 }
4808               });
4809               return result;
4810             }
4811
4812             function getYOffset() {
4813
4814               var offset = scroll.yOffset;
4815
4816               if (isFunction(offset)) {
4817                 offset = offset();
4818               } else if (isElement(offset)) {
4819                 var elem = offset[0];
4820                 var style = $window.getComputedStyle(elem);
4821                 if (style.position !== 'fixed') {
4822                   offset = 0;
4823                 } else {
4824                   offset = elem.getBoundingClientRect().bottom;
4825                 }
4826               } else if (!isNumber(offset)) {
4827                 offset = 0;
4828               }
4829
4830               return offset;
4831             }
4832
4833             function scrollTo(elem) {
4834               if (elem) {
4835                 elem.scrollIntoView();
4836
4837                 var offset = getYOffset();
4838
4839                 if (offset) {
4840                   // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4841                   // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4842                   // top of the viewport.
4843                   //
4844                   // IF the number of pixels from the top of `elem` to the end of the page's content is less
4845                   // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4846                   // way down the page.
4847                   //
4848                   // This is often the case for elements near the bottom of the page.
4849                   //
4850                   // In such cases we do not need to scroll the whole `offset` up, just the difference between
4851                   // the top of the element and the offset, which is enough to align the top of `elem` at the
4852                   // desired position.
4853                   var elemTop = elem.getBoundingClientRect().top;
4854                   $window.scrollBy(0, elemTop - offset);
4855                 }
4856               } else {
4857                 $window.scrollTo(0, 0);
4858               }
4859             }
4860
4861             function scroll(hash) {
4862               hash = isString(hash) ? hash : $location.hash();
4863               var elm;
4864
4865               // empty hash, scroll to the top of the page
4866               if (!hash) scrollTo(null);
4867
4868               // element with given id
4869               else if ((elm = document.getElementById(hash))) scrollTo(elm);
4870
4871               // first anchor with given name :-D
4872               else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4873
4874               // no element and hash == 'top', scroll to the top of the page
4875               else if (hash === 'top') scrollTo(null);
4876             }
4877
4878             // does not scroll when user clicks on anchor link that is currently on
4879             // (no url change, no $location.hash() change), browser native does scroll
4880             if (autoScrollingEnabled) {
4881               $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4882                 function autoScrollWatchAction(newVal, oldVal) {
4883                   // skip the initial scroll if $location.hash is empty
4884                   if (newVal === oldVal && newVal === '') return;
4885
4886                   jqLiteDocumentLoaded(function() {
4887                     $rootScope.$evalAsync(scroll);
4888                   });
4889                 });
4890             }
4891
4892             return scroll;
4893           }];
4894         }
4895
4896         var $animateMinErr = minErr('$animate');
4897         var ELEMENT_NODE = 1;
4898         var NG_ANIMATE_CLASSNAME = 'ng-animate';
4899
4900         function mergeClasses(a,b) {
4901           if (!a && !b) return '';
4902           if (!a) return b;
4903           if (!b) return a;
4904           if (isArray(a)) a = a.join(' ');
4905           if (isArray(b)) b = b.join(' ');
4906           return a + ' ' + b;
4907         }
4908
4909         function extractElementNode(element) {
4910           for (var i = 0; i < element.length; i++) {
4911             var elm = element[i];
4912             if (elm.nodeType === ELEMENT_NODE) {
4913               return elm;
4914             }
4915           }
4916         }
4917
4918         function splitClasses(classes) {
4919           if (isString(classes)) {
4920             classes = classes.split(' ');
4921           }
4922
4923           // Use createMap() to prevent class assumptions involving property names in
4924           // Object.prototype
4925           var obj = createMap();
4926           forEach(classes, function(klass) {
4927             // sometimes the split leaves empty string values
4928             // incase extra spaces were applied to the options
4929             if (klass.length) {
4930               obj[klass] = true;
4931             }
4932           });
4933           return obj;
4934         }
4935
4936         // if any other type of options value besides an Object value is
4937         // passed into the $animate.method() animation then this helper code
4938         // will be run which will ignore it. While this patch is not the
4939         // greatest solution to this, a lot of existing plugins depend on
4940         // $animate to either call the callback (< 1.2) or return a promise
4941         // that can be changed. This helper function ensures that the options
4942         // are wiped clean incase a callback function is provided.
4943         function prepareAnimateOptions(options) {
4944           return isObject(options)
4945               ? options
4946               : {};
4947         }
4948
4949         var $$CoreAnimateRunnerProvider = function() {
4950           this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4951             function AnimateRunner() {}
4952             AnimateRunner.all = noop;
4953             AnimateRunner.chain = noop;
4954             AnimateRunner.prototype = {
4955               end: noop,
4956               cancel: noop,
4957               resume: noop,
4958               pause: noop,
4959               complete: noop,
4960               then: function(pass, fail) {
4961                 return $q(function(resolve) {
4962                   $$rAF(function() {
4963                     resolve();
4964                   });
4965                 }).then(pass, fail);
4966               }
4967             };
4968             return AnimateRunner;
4969           }];
4970         };
4971
4972         // this is prefixed with Core since it conflicts with
4973         // the animateQueueProvider defined in ngAnimate/animateQueue.js
4974         var $$CoreAnimateQueueProvider = function() {
4975           var postDigestQueue = new HashMap();
4976           var postDigestElements = [];
4977
4978           this.$get = ['$$AnimateRunner', '$rootScope',
4979                function($$AnimateRunner,   $rootScope) {
4980             return {
4981               enabled: noop,
4982               on: noop,
4983               off: noop,
4984               pin: noop,
4985
4986               push: function(element, event, options, domOperation) {
4987                 domOperation        && domOperation();
4988
4989                 options = options || {};
4990                 options.from        && element.css(options.from);
4991                 options.to          && element.css(options.to);
4992
4993                 if (options.addClass || options.removeClass) {
4994                   addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4995                 }
4996
4997                 return new $$AnimateRunner(); // jshint ignore:line
4998               }
4999             };
5000
5001
5002             function updateData(data, classes, value) {
5003               var changed = false;
5004               if (classes) {
5005                 classes = isString(classes) ? classes.split(' ') :
5006                           isArray(classes) ? classes : [];
5007                 forEach(classes, function(className) {
5008                   if (className) {
5009                     changed = true;
5010                     data[className] = value;
5011                   }
5012                 });
5013               }
5014               return changed;
5015             }
5016
5017             function handleCSSClassChanges() {
5018               forEach(postDigestElements, function(element) {
5019                 var data = postDigestQueue.get(element);
5020                 if (data) {
5021                   var existing = splitClasses(element.attr('class'));
5022                   var toAdd = '';
5023                   var toRemove = '';
5024                   forEach(data, function(status, className) {
5025                     var hasClass = !!existing[className];
5026                     if (status !== hasClass) {
5027                       if (status) {
5028                         toAdd += (toAdd.length ? ' ' : '') + className;
5029                       } else {
5030                         toRemove += (toRemove.length ? ' ' : '') + className;
5031                       }
5032                     }
5033                   });
5034
5035                   forEach(element, function(elm) {
5036                     toAdd    && jqLiteAddClass(elm, toAdd);
5037                     toRemove && jqLiteRemoveClass(elm, toRemove);
5038                   });
5039                   postDigestQueue.remove(element);
5040                 }
5041               });
5042               postDigestElements.length = 0;
5043             }
5044
5045
5046             function addRemoveClassesPostDigest(element, add, remove) {
5047               var data = postDigestQueue.get(element) || {};
5048
5049               var classesAdded = updateData(data, add, true);
5050               var classesRemoved = updateData(data, remove, false);
5051
5052               if (classesAdded || classesRemoved) {
5053
5054                 postDigestQueue.put(element, data);
5055                 postDigestElements.push(element);
5056
5057                 if (postDigestElements.length === 1) {
5058                   $rootScope.$$postDigest(handleCSSClassChanges);
5059                 }
5060               }
5061             }
5062           }];
5063         };
5064
5065         /**
5066          * @ngdoc provider
5067          * @name $animateProvider
5068          *
5069          * @description
5070          * Default implementation of $animate that doesn't perform any animations, instead just
5071          * synchronously performs DOM updates and resolves the returned runner promise.
5072          *
5073          * In order to enable animations the `ngAnimate` module has to be loaded.
5074          *
5075          * To see the functional implementation check out `src/ngAnimate/animate.js`.
5076          */
5077         var $AnimateProvider = ['$provide', function($provide) {
5078           var provider = this;
5079
5080           this.$$registeredAnimations = Object.create(null);
5081
5082            /**
5083            * @ngdoc method
5084            * @name $animateProvider#register
5085            *
5086            * @description
5087            * Registers a new injectable animation factory function. The factory function produces the
5088            * animation object which contains callback functions for each event that is expected to be
5089            * animated.
5090            *
5091            *   * `eventFn`: `function(element, ... , doneFunction, options)`
5092            *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5093            *   on the type of animation additional arguments will be injected into the animation function. The
5094            *   list below explains the function signatures for the different animation methods:
5095            *
5096            *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5097            *   - addClass: function(element, addedClasses, doneFunction, options)
5098            *   - removeClass: function(element, removedClasses, doneFunction, options)
5099            *   - enter, leave, move: function(element, doneFunction, options)
5100            *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5101            *
5102            *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5103            *
5104            * ```js
5105            *   return {
5106            *     //enter, leave, move signature
5107            *     eventFn : function(element, done, options) {
5108            *       //code to run the animation
5109            *       //once complete, then run done()
5110            *       return function endFunction(wasCancelled) {
5111            *         //code to cancel the animation
5112            *       }
5113            *     }
5114            *   }
5115            * ```
5116            *
5117            * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5118            * @param {Function} factory The factory function that will be executed to return the animation
5119            *                           object.
5120            */
5121           this.register = function(name, factory) {
5122             if (name && name.charAt(0) !== '.') {
5123               throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5124             }
5125
5126             var key = name + '-animation';
5127             provider.$$registeredAnimations[name.substr(1)] = key;
5128             $provide.factory(key, factory);
5129           };
5130
5131           /**
5132            * @ngdoc method
5133            * @name $animateProvider#classNameFilter
5134            *
5135            * @description
5136            * Sets and/or returns the CSS class regular expression that is checked when performing
5137            * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5138            * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5139            * When setting the `classNameFilter` value, animations will only be performed on elements
5140            * that successfully match the filter expression. This in turn can boost performance
5141            * for low-powered devices as well as applications containing a lot of structural operations.
5142            * @param {RegExp=} expression The className expression which will be checked against all animations
5143            * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5144            */
5145           this.classNameFilter = function(expression) {
5146             if (arguments.length === 1) {
5147               this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5148               if (this.$$classNameFilter) {
5149                 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5150                 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5151                   throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5152
5153                 }
5154               }
5155             }
5156             return this.$$classNameFilter;
5157           };
5158
5159           this.$get = ['$$animateQueue', function($$animateQueue) {
5160             function domInsert(element, parentElement, afterElement) {
5161               // if for some reason the previous element was removed
5162               // from the dom sometime before this code runs then let's
5163               // just stick to using the parent element as the anchor
5164               if (afterElement) {
5165                 var afterNode = extractElementNode(afterElement);
5166                 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5167                   afterElement = null;
5168                 }
5169               }
5170               afterElement ? afterElement.after(element) : parentElement.prepend(element);
5171             }
5172
5173             /**
5174              * @ngdoc service
5175              * @name $animate
5176              * @description The $animate service exposes a series of DOM utility methods that provide support
5177              * for animation hooks. The default behavior is the application of DOM operations, however,
5178              * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5179              * to ensure that animation runs with the triggered DOM operation.
5180              *
5181              * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5182              * included and only when it is active then the animation hooks that `$animate` triggers will be
5183              * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5184              * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5185              * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5186              *
5187              * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5188              *
5189              * To learn more about enabling animation support, click here to visit the
5190              * {@link ngAnimate ngAnimate module page}.
5191              */
5192             return {
5193               // we don't call it directly since non-existant arguments may
5194               // be interpreted as null within the sub enabled function
5195
5196               /**
5197                *
5198                * @ngdoc method
5199                * @name $animate#on
5200                * @kind function
5201                * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5202                *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5203                *    is fired with the following params:
5204                *
5205                * ```js
5206                * $animate.on('enter', container,
5207                *    function callback(element, phase) {
5208                *      // cool we detected an enter animation within the container
5209                *    }
5210                * );
5211                * ```
5212                *
5213                * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5214                * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5215                *     as well as among its children
5216                * @param {Function} callback the callback function that will be fired when the listener is triggered
5217                *
5218                * The arguments present in the callback function are:
5219                * * `element` - The captured DOM element that the animation was fired on.
5220                * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5221                */
5222               on: $$animateQueue.on,
5223
5224               /**
5225                *
5226                * @ngdoc method
5227                * @name $animate#off
5228                * @kind function
5229                * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5230                * can be used in three different ways depending on the arguments:
5231                *
5232                * ```js
5233                * // remove all the animation event listeners listening for `enter`
5234                * $animate.off('enter');
5235                *
5236                * // remove all the animation event listeners listening for `enter` on the given element and its children
5237                * $animate.off('enter', container);
5238                *
5239                * // remove the event listener function provided by `listenerFn` that is set
5240                * // to listen for `enter` on the given `element` as well as its children
5241                * $animate.off('enter', container, callback);
5242                * ```
5243                *
5244                * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5245                * @param {DOMElement=} container the container element the event listener was placed on
5246                * @param {Function=} callback the callback function that was registered as the listener
5247                */
5248               off: $$animateQueue.off,
5249
5250               /**
5251                * @ngdoc method
5252                * @name $animate#pin
5253                * @kind function
5254                * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5255                *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5256                *    element despite being outside the realm of the application or within another application. Say for example if the application
5257                *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5258                *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5259                *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5260                *
5261                *    Note that this feature is only active when the `ngAnimate` module is used.
5262                *
5263                * @param {DOMElement} element the external element that will be pinned
5264                * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5265                */
5266               pin: $$animateQueue.pin,
5267
5268               /**
5269                *
5270                * @ngdoc method
5271                * @name $animate#enabled
5272                * @kind function
5273                * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5274                * function can be called in four ways:
5275                *
5276                * ```js
5277                * // returns true or false
5278                * $animate.enabled();
5279                *
5280                * // changes the enabled state for all animations
5281                * $animate.enabled(false);
5282                * $animate.enabled(true);
5283                *
5284                * // returns true or false if animations are enabled for an element
5285                * $animate.enabled(element);
5286                *
5287                * // changes the enabled state for an element and its children
5288                * $animate.enabled(element, true);
5289                * $animate.enabled(element, false);
5290                * ```
5291                *
5292                * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5293                * @param {boolean=} enabled whether or not the animations will be enabled for the element
5294                *
5295                * @return {boolean} whether or not animations are enabled
5296                */
5297               enabled: $$animateQueue.enabled,
5298
5299               /**
5300                * @ngdoc method
5301                * @name $animate#cancel
5302                * @kind function
5303                * @description Cancels the provided animation.
5304                *
5305                * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5306                */
5307               cancel: function(runner) {
5308                 runner.end && runner.end();
5309               },
5310
5311               /**
5312                *
5313                * @ngdoc method
5314                * @name $animate#enter
5315                * @kind function
5316                * @description Inserts the element into the DOM either after the `after` element (if provided) or
5317                *   as the first child within the `parent` element and then triggers an animation.
5318                *   A promise is returned that will be resolved during the next digest once the animation
5319                *   has completed.
5320                *
5321                * @param {DOMElement} element the element which will be inserted into the DOM
5322                * @param {DOMElement} parent the parent element which will append the element as
5323                *   a child (so long as the after element is not present)
5324                * @param {DOMElement=} after the sibling element after which the element will be appended
5325                * @param {object=} options an optional collection of options/styles that will be applied to the element
5326                *
5327                * @return {Promise} the animation callback promise
5328                */
5329               enter: function(element, parent, after, options) {
5330                 parent = parent && jqLite(parent);
5331                 after = after && jqLite(after);
5332                 parent = parent || after.parent();
5333                 domInsert(element, parent, after);
5334                 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5335               },
5336
5337               /**
5338                *
5339                * @ngdoc method
5340                * @name $animate#move
5341                * @kind function
5342                * @description Inserts (moves) the element into its new position in the DOM either after
5343                *   the `after` element (if provided) or as the first child within the `parent` element
5344                *   and then triggers an animation. A promise is returned that will be resolved
5345                *   during the next digest once the animation has completed.
5346                *
5347                * @param {DOMElement} element the element which will be moved into the new DOM position
5348                * @param {DOMElement} parent the parent element which will append the element as
5349                *   a child (so long as the after element is not present)
5350                * @param {DOMElement=} after the sibling element after which the element will be appended
5351                * @param {object=} options an optional collection of options/styles that will be applied to the element
5352                *
5353                * @return {Promise} the animation callback promise
5354                */
5355               move: function(element, parent, after, options) {
5356                 parent = parent && jqLite(parent);
5357                 after = after && jqLite(after);
5358                 parent = parent || after.parent();
5359                 domInsert(element, parent, after);
5360                 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5361               },
5362
5363               /**
5364                * @ngdoc method
5365                * @name $animate#leave
5366                * @kind function
5367                * @description Triggers an animation and then removes the element from the DOM.
5368                * When the function is called a promise is returned that will be resolved during the next
5369                * digest once the animation has completed.
5370                *
5371                * @param {DOMElement} element the element which will be removed from the DOM
5372                * @param {object=} options an optional collection of options/styles that will be applied to the element
5373                *
5374                * @return {Promise} the animation callback promise
5375                */
5376               leave: function(element, options) {
5377                 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5378                   element.remove();
5379                 });
5380               },
5381
5382               /**
5383                * @ngdoc method
5384                * @name $animate#addClass
5385                * @kind function
5386                *
5387                * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5388                *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5389                *   animation if element already contains the CSS class or if the class is removed at a later step.
5390                *   Note that class-based animations are treated differently compared to structural animations
5391                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5392                *   depending if CSS or JavaScript animations are used.
5393                *
5394                * @param {DOMElement} element the element which the CSS classes will be applied to
5395                * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5396                * @param {object=} options an optional collection of options/styles that will be applied to the element
5397                *
5398                * @return {Promise} the animation callback promise
5399                */
5400               addClass: function(element, className, options) {
5401                 options = prepareAnimateOptions(options);
5402                 options.addClass = mergeClasses(options.addclass, className);
5403                 return $$animateQueue.push(element, 'addClass', options);
5404               },
5405
5406               /**
5407                * @ngdoc method
5408                * @name $animate#removeClass
5409                * @kind function
5410                *
5411                * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5412                *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5413                *   animation if element does not contain the CSS class or if the class is added at a later step.
5414                *   Note that class-based animations are treated differently compared to structural animations
5415                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5416                *   depending if CSS or JavaScript animations are used.
5417                *
5418                * @param {DOMElement} element the element which the CSS classes will be applied to
5419                * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5420                * @param {object=} options an optional collection of options/styles that will be applied to the element
5421                *
5422                * @return {Promise} the animation callback promise
5423                */
5424               removeClass: function(element, className, options) {
5425                 options = prepareAnimateOptions(options);
5426                 options.removeClass = mergeClasses(options.removeClass, className);
5427                 return $$animateQueue.push(element, 'removeClass', options);
5428               },
5429
5430               /**
5431                * @ngdoc method
5432                * @name $animate#setClass
5433                * @kind function
5434                *
5435                * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5436                *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5437                *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5438                *    passed. Note that class-based animations are treated differently compared to structural animations
5439                *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5440                *    depending if CSS or JavaScript animations are used.
5441                *
5442                * @param {DOMElement} element the element which the CSS classes will be applied to
5443                * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5444                * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5445                * @param {object=} options an optional collection of options/styles that will be applied to the element
5446                *
5447                * @return {Promise} the animation callback promise
5448                */
5449               setClass: function(element, add, remove, options) {
5450                 options = prepareAnimateOptions(options);
5451                 options.addClass = mergeClasses(options.addClass, add);
5452                 options.removeClass = mergeClasses(options.removeClass, remove);
5453                 return $$animateQueue.push(element, 'setClass', options);
5454               },
5455
5456               /**
5457                * @ngdoc method
5458                * @name $animate#animate
5459                * @kind function
5460                *
5461                * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5462                * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5463                * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5464                * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5465                * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5466                *
5467                * @param {DOMElement} element the element which the CSS styles will be applied to
5468                * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5469                * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5470                * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5471                *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5472                *    (Note that if no animation is detected then this value will not be appplied to the element.)
5473                * @param {object=} options an optional collection of options/styles that will be applied to the element
5474                *
5475                * @return {Promise} the animation callback promise
5476                */
5477               animate: function(element, from, to, className, options) {
5478                 options = prepareAnimateOptions(options);
5479                 options.from = options.from ? extend(options.from, from) : from;
5480                 options.to   = options.to   ? extend(options.to, to)     : to;
5481
5482                 className = className || 'ng-inline-animate';
5483                 options.tempClasses = mergeClasses(options.tempClasses, className);
5484                 return $$animateQueue.push(element, 'animate', options);
5485               }
5486             };
5487           }];
5488         }];
5489
5490         /**
5491          * @ngdoc service
5492          * @name $animateCss
5493          * @kind object
5494          *
5495          * @description
5496          * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5497          * then the `$animateCss` service will actually perform animations.
5498          *
5499          * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5500          */
5501         var $CoreAnimateCssProvider = function() {
5502           this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5503
5504             var RAFPromise = function() {};
5505             RAFPromise.prototype = {
5506               done: function(cancel) {
5507                 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5508               },
5509               end: function() {
5510                 this.done();
5511               },
5512               cancel: function() {
5513                 this.done(true);
5514               },
5515               getPromise: function() {
5516                 if (!this.defer) {
5517                   this.defer = $q.defer();
5518                 }
5519                 return this.defer.promise;
5520               },
5521               then: function(f1,f2) {
5522                 return this.getPromise().then(f1,f2);
5523               },
5524               'catch': function(f1) {
5525                 return this.getPromise()['catch'](f1);
5526               },
5527               'finally': function(f1) {
5528                 return this.getPromise()['finally'](f1);
5529               }
5530             };
5531
5532             return function(element, options) {
5533               // there is no point in applying the styles since
5534               // there is no animation that goes on at all in
5535               // this version of $animateCss.
5536               if (options.cleanupStyles) {
5537                 options.from = options.to = null;
5538               }
5539
5540               if (options.from) {
5541                 element.css(options.from);
5542                 options.from = null;
5543               }
5544
5545               var closed, runner = new RAFPromise();
5546               return {
5547                 start: run,
5548                 end: run
5549               };
5550
5551               function run() {
5552                 $$rAF(function() {
5553                   close();
5554                   if (!closed) {
5555                     runner.done();
5556                   }
5557                   closed = true;
5558                 });
5559                 return runner;
5560               }
5561
5562               function close() {
5563                 if (options.addClass) {
5564                   element.addClass(options.addClass);
5565                   options.addClass = null;
5566                 }
5567                 if (options.removeClass) {
5568                   element.removeClass(options.removeClass);
5569                   options.removeClass = null;
5570                 }
5571                 if (options.to) {
5572                   element.css(options.to);
5573                   options.to = null;
5574                 }
5575               }
5576             };
5577           }];
5578         };
5579
5580         /* global stripHash: true */
5581
5582         /**
5583          * ! This is a private undocumented service !
5584          *
5585          * @name $browser
5586          * @requires $log
5587          * @description
5588          * This object has two goals:
5589          *
5590          * - hide all the global state in the browser caused by the window object
5591          * - abstract away all the browser specific features and inconsistencies
5592          *
5593          * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5594          * service, which can be used for convenient testing of the application without the interaction with
5595          * the real browser apis.
5596          */
5597         /**
5598          * @param {object} window The global window object.
5599          * @param {object} document jQuery wrapped document.
5600          * @param {object} $log window.console or an object with the same interface.
5601          * @param {object} $sniffer $sniffer service
5602          */
5603         function Browser(window, document, $log, $sniffer) {
5604           var self = this,
5605               rawDocument = document[0],
5606               location = window.location,
5607               history = window.history,
5608               setTimeout = window.setTimeout,
5609               clearTimeout = window.clearTimeout,
5610               pendingDeferIds = {};
5611
5612           self.isMock = false;
5613
5614           var outstandingRequestCount = 0;
5615           var outstandingRequestCallbacks = [];
5616
5617           // TODO(vojta): remove this temporary api
5618           self.$$completeOutstandingRequest = completeOutstandingRequest;
5619           self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5620
5621           /**
5622            * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5623            * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5624            */
5625           function completeOutstandingRequest(fn) {
5626             try {
5627               fn.apply(null, sliceArgs(arguments, 1));
5628             } finally {
5629               outstandingRequestCount--;
5630               if (outstandingRequestCount === 0) {
5631                 while (outstandingRequestCallbacks.length) {
5632                   try {
5633                     outstandingRequestCallbacks.pop()();
5634                   } catch (e) {
5635                     $log.error(e);
5636                   }
5637                 }
5638               }
5639             }
5640           }
5641
5642           function getHash(url) {
5643             var index = url.indexOf('#');
5644             return index === -1 ? '' : url.substr(index);
5645           }
5646
5647           /**
5648            * @private
5649            * Note: this method is used only by scenario runner
5650            * TODO(vojta): prefix this method with $$ ?
5651            * @param {function()} callback Function that will be called when no outstanding request
5652            */
5653           self.notifyWhenNoOutstandingRequests = function(callback) {
5654             if (outstandingRequestCount === 0) {
5655               callback();
5656             } else {
5657               outstandingRequestCallbacks.push(callback);
5658             }
5659           };
5660
5661           //////////////////////////////////////////////////////////////
5662           // URL API
5663           //////////////////////////////////////////////////////////////
5664
5665           var cachedState, lastHistoryState,
5666               lastBrowserUrl = location.href,
5667               baseElement = document.find('base'),
5668               pendingLocation = null;
5669
5670           cacheState();
5671           lastHistoryState = cachedState;
5672
5673           /**
5674            * @name $browser#url
5675            *
5676            * @description
5677            * GETTER:
5678            * Without any argument, this method just returns current value of location.href.
5679            *
5680            * SETTER:
5681            * With at least one argument, this method sets url to new value.
5682            * If html5 history api supported, pushState/replaceState is used, otherwise
5683            * location.href/location.replace is used.
5684            * Returns its own instance to allow chaining
5685            *
5686            * NOTE: this api is intended for use only by the $location service. Please use the
5687            * {@link ng.$location $location service} to change url.
5688            *
5689            * @param {string} url New url (when used as setter)
5690            * @param {boolean=} replace Should new url replace current history record?
5691            * @param {object=} state object to use with pushState/replaceState
5692            */
5693           self.url = function(url, replace, state) {
5694             // In modern browsers `history.state` is `null` by default; treating it separately
5695             // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5696             // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5697             if (isUndefined(state)) {
5698               state = null;
5699             }
5700
5701             // Android Browser BFCache causes location, history reference to become stale.
5702             if (location !== window.location) location = window.location;
5703             if (history !== window.history) history = window.history;
5704
5705             // setter
5706             if (url) {
5707               var sameState = lastHistoryState === state;
5708
5709               // Don't change anything if previous and current URLs and states match. This also prevents
5710               // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5711               // See https://github.com/angular/angular.js/commit/ffb2701
5712               if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5713                 return self;
5714               }
5715               var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5716               lastBrowserUrl = url;
5717               lastHistoryState = state;
5718               // Don't use history API if only the hash changed
5719               // due to a bug in IE10/IE11 which leads
5720               // to not firing a `hashchange` nor `popstate` event
5721               // in some cases (see #9143).
5722               if ($sniffer.history && (!sameBase || !sameState)) {
5723                 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5724                 cacheState();
5725                 // Do the assignment again so that those two variables are referentially identical.
5726                 lastHistoryState = cachedState;
5727               } else {
5728                 if (!sameBase || pendingLocation) {
5729                   pendingLocation = url;
5730                 }
5731                 if (replace) {
5732                   location.replace(url);
5733                 } else if (!sameBase) {
5734                   location.href = url;
5735                 } else {
5736                   location.hash = getHash(url);
5737                 }
5738                 if (location.href !== url) {
5739                   pendingLocation = url;
5740                 }
5741               }
5742               return self;
5743             // getter
5744             } else {
5745               // - pendingLocation is needed as browsers don't allow to read out
5746               //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5747               //   https://openradar.appspot.com/22186109).
5748               // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5749               return pendingLocation || location.href.replace(/%27/g,"'");
5750             }
5751           };
5752
5753           /**
5754            * @name $browser#state
5755            *
5756            * @description
5757            * This method is a getter.
5758            *
5759            * Return history.state or null if history.state is undefined.
5760            *
5761            * @returns {object} state
5762            */
5763           self.state = function() {
5764             return cachedState;
5765           };
5766
5767           var urlChangeListeners = [],
5768               urlChangeInit = false;
5769
5770           function cacheStateAndFireUrlChange() {
5771             pendingLocation = null;
5772             cacheState();
5773             fireUrlChange();
5774           }
5775
5776           function getCurrentState() {
5777             try {
5778               return history.state;
5779             } catch (e) {
5780               // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5781             }
5782           }
5783
5784           // This variable should be used *only* inside the cacheState function.
5785           var lastCachedState = null;
5786           function cacheState() {
5787             // This should be the only place in $browser where `history.state` is read.
5788             cachedState = getCurrentState();
5789             cachedState = isUndefined(cachedState) ? null : cachedState;
5790
5791             // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5792             if (equals(cachedState, lastCachedState)) {
5793               cachedState = lastCachedState;
5794             }
5795             lastCachedState = cachedState;
5796           }
5797
5798           function fireUrlChange() {
5799             if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5800               return;
5801             }
5802
5803             lastBrowserUrl = self.url();
5804             lastHistoryState = cachedState;
5805             forEach(urlChangeListeners, function(listener) {
5806               listener(self.url(), cachedState);
5807             });
5808           }
5809
5810           /**
5811            * @name $browser#onUrlChange
5812            *
5813            * @description
5814            * Register callback function that will be called, when url changes.
5815            *
5816            * It's only called when the url is changed from outside of angular:
5817            * - user types different url into address bar
5818            * - user clicks on history (forward/back) button
5819            * - user clicks on a link
5820            *
5821            * It's not called when url is changed by $browser.url() method
5822            *
5823            * The listener gets called with new url as parameter.
5824            *
5825            * NOTE: this api is intended for use only by the $location service. Please use the
5826            * {@link ng.$location $location service} to monitor url changes in angular apps.
5827            *
5828            * @param {function(string)} listener Listener function to be called when url changes.
5829            * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5830            */
5831           self.onUrlChange = function(callback) {
5832             // TODO(vojta): refactor to use node's syntax for events
5833             if (!urlChangeInit) {
5834               // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5835               // don't fire popstate when user change the address bar and don't fire hashchange when url
5836               // changed by push/replaceState
5837
5838               // html5 history api - popstate event
5839               if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5840               // hashchange event
5841               jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5842
5843               urlChangeInit = true;
5844             }
5845
5846             urlChangeListeners.push(callback);
5847             return callback;
5848           };
5849
5850           /**
5851            * @private
5852            * Remove popstate and hashchange handler from window.
5853            *
5854            * NOTE: this api is intended for use only by $rootScope.
5855            */
5856           self.$$applicationDestroyed = function() {
5857             jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5858           };
5859
5860           /**
5861            * Checks whether the url has changed outside of Angular.
5862            * Needs to be exported to be able to check for changes that have been done in sync,
5863            * as hashchange/popstate events fire in async.
5864            */
5865           self.$$checkUrlChange = fireUrlChange;
5866
5867           //////////////////////////////////////////////////////////////
5868           // Misc API
5869           //////////////////////////////////////////////////////////////
5870
5871           /**
5872            * @name $browser#baseHref
5873            *
5874            * @description
5875            * Returns current <base href>
5876            * (always relative - without domain)
5877            *
5878            * @returns {string} The current base href
5879            */
5880           self.baseHref = function() {
5881             var href = baseElement.attr('href');
5882             return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5883           };
5884
5885           /**
5886            * @name $browser#defer
5887            * @param {function()} fn A function, who's execution should be deferred.
5888            * @param {number=} [delay=0] of milliseconds to defer the function execution.
5889            * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5890            *
5891            * @description
5892            * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5893            *
5894            * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5895            * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5896            * via `$browser.defer.flush()`.
5897            *
5898            */
5899           self.defer = function(fn, delay) {
5900             var timeoutId;
5901             outstandingRequestCount++;
5902             timeoutId = setTimeout(function() {
5903               delete pendingDeferIds[timeoutId];
5904               completeOutstandingRequest(fn);
5905             }, delay || 0);
5906             pendingDeferIds[timeoutId] = true;
5907             return timeoutId;
5908           };
5909
5910
5911           /**
5912            * @name $browser#defer.cancel
5913            *
5914            * @description
5915            * Cancels a deferred task identified with `deferId`.
5916            *
5917            * @param {*} deferId Token returned by the `$browser.defer` function.
5918            * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5919            *                    canceled.
5920            */
5921           self.defer.cancel = function(deferId) {
5922             if (pendingDeferIds[deferId]) {
5923               delete pendingDeferIds[deferId];
5924               clearTimeout(deferId);
5925               completeOutstandingRequest(noop);
5926               return true;
5927             }
5928             return false;
5929           };
5930
5931         }
5932
5933         function $BrowserProvider() {
5934           this.$get = ['$window', '$log', '$sniffer', '$document',
5935               function($window, $log, $sniffer, $document) {
5936                 return new Browser($window, $document, $log, $sniffer);
5937               }];
5938         }
5939
5940         /**
5941          * @ngdoc service
5942          * @name $cacheFactory
5943          *
5944          * @description
5945          * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5946          * them.
5947          *
5948          * ```js
5949          *
5950          *  var cache = $cacheFactory('cacheId');
5951          *  expect($cacheFactory.get('cacheId')).toBe(cache);
5952          *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5953          *
5954          *  cache.put("key", "value");
5955          *  cache.put("another key", "another value");
5956          *
5957          *  // We've specified no options on creation
5958          *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5959          *
5960          * ```
5961          *
5962          *
5963          * @param {string} cacheId Name or id of the newly created cache.
5964          * @param {object=} options Options object that specifies the cache behavior. Properties:
5965          *
5966          *   - `{number=}` `capacity` — turns the cache into LRU cache.
5967          *
5968          * @returns {object} Newly created cache object with the following set of methods:
5969          *
5970          * - `{object}` `info()` — Returns id, size, and options of cache.
5971          * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5972          *   it.
5973          * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5974          * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5975          * - `{void}` `removeAll()` — Removes all cached values.
5976          * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5977          *
5978          * @example
5979            <example module="cacheExampleApp">
5980              <file name="index.html">
5981                <div ng-controller="CacheController">
5982                  <input ng-model="newCacheKey" placeholder="Key">
5983                  <input ng-model="newCacheValue" placeholder="Value">
5984                  <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5985
5986                  <p ng-if="keys.length">Cached Values</p>
5987                  <div ng-repeat="key in keys">
5988                    <span ng-bind="key"></span>
5989                    <span>: </span>
5990                    <b ng-bind="cache.get(key)"></b>
5991                  </div>
5992
5993                  <p>Cache Info</p>
5994                  <div ng-repeat="(key, value) in cache.info()">
5995                    <span ng-bind="key"></span>
5996                    <span>: </span>
5997                    <b ng-bind="value"></b>
5998                  </div>
5999                </div>
6000              </file>
6001              <file name="script.js">
6002                angular.module('cacheExampleApp', []).
6003                  controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6004                    $scope.keys = [];
6005                    $scope.cache = $cacheFactory('cacheId');
6006                    $scope.put = function(key, value) {
6007                      if (angular.isUndefined($scope.cache.get(key))) {
6008                        $scope.keys.push(key);
6009                      }
6010                      $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6011                    };
6012                  }]);
6013              </file>
6014              <file name="style.css">
6015                p {
6016                  margin: 10px 0 3px;
6017                }
6018              </file>
6019            </example>
6020          */
6021         function $CacheFactoryProvider() {
6022
6023           this.$get = function() {
6024             var caches = {};
6025
6026             function cacheFactory(cacheId, options) {
6027               if (cacheId in caches) {
6028                 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6029               }
6030
6031               var size = 0,
6032                   stats = extend({}, options, {id: cacheId}),
6033                   data = createMap(),
6034                   capacity = (options && options.capacity) || Number.MAX_VALUE,
6035                   lruHash = createMap(),
6036                   freshEnd = null,
6037                   staleEnd = null;
6038
6039               /**
6040                * @ngdoc type
6041                * @name $cacheFactory.Cache
6042                *
6043                * @description
6044                * A cache object used to store and retrieve data, primarily used by
6045                * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6046                * templates and other data.
6047                *
6048                * ```js
6049                *  angular.module('superCache')
6050                *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6051                *      return $cacheFactory('super-cache');
6052                *    }]);
6053                * ```
6054                *
6055                * Example test:
6056                *
6057                * ```js
6058                *  it('should behave like a cache', inject(function(superCache) {
6059                *    superCache.put('key', 'value');
6060                *    superCache.put('another key', 'another value');
6061                *
6062                *    expect(superCache.info()).toEqual({
6063                *      id: 'super-cache',
6064                *      size: 2
6065                *    });
6066                *
6067                *    superCache.remove('another key');
6068                *    expect(superCache.get('another key')).toBeUndefined();
6069                *
6070                *    superCache.removeAll();
6071                *    expect(superCache.info()).toEqual({
6072                *      id: 'super-cache',
6073                *      size: 0
6074                *    });
6075                *  }));
6076                * ```
6077                */
6078               return caches[cacheId] = {
6079
6080                 /**
6081                  * @ngdoc method
6082                  * @name $cacheFactory.Cache#put
6083                  * @kind function
6084                  *
6085                  * @description
6086                  * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6087                  * retrieved later, and incrementing the size of the cache if the key was not already
6088                  * present in the cache. If behaving like an LRU cache, it will also remove stale
6089                  * entries from the set.
6090                  *
6091                  * It will not insert undefined values into the cache.
6092                  *
6093                  * @param {string} key the key under which the cached data is stored.
6094                  * @param {*} value the value to store alongside the key. If it is undefined, the key
6095                  *    will not be stored.
6096                  * @returns {*} the value stored.
6097                  */
6098                 put: function(key, value) {
6099                   if (isUndefined(value)) return;
6100                   if (capacity < Number.MAX_VALUE) {
6101                     var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6102
6103                     refresh(lruEntry);
6104                   }
6105
6106                   if (!(key in data)) size++;
6107                   data[key] = value;
6108
6109                   if (size > capacity) {
6110                     this.remove(staleEnd.key);
6111                   }
6112
6113                   return value;
6114                 },
6115
6116                 /**
6117                  * @ngdoc method
6118                  * @name $cacheFactory.Cache#get
6119                  * @kind function
6120                  *
6121                  * @description
6122                  * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6123                  *
6124                  * @param {string} key the key of the data to be retrieved
6125                  * @returns {*} the value stored.
6126                  */
6127                 get: function(key) {
6128                   if (capacity < Number.MAX_VALUE) {
6129                     var lruEntry = lruHash[key];
6130
6131                     if (!lruEntry) return;
6132
6133                     refresh(lruEntry);
6134                   }
6135
6136                   return data[key];
6137                 },
6138
6139
6140                 /**
6141                  * @ngdoc method
6142                  * @name $cacheFactory.Cache#remove
6143                  * @kind function
6144                  *
6145                  * @description
6146                  * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6147                  *
6148                  * @param {string} key the key of the entry to be removed
6149                  */
6150                 remove: function(key) {
6151                   if (capacity < Number.MAX_VALUE) {
6152                     var lruEntry = lruHash[key];
6153
6154                     if (!lruEntry) return;
6155
6156                     if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6157                     if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6158                     link(lruEntry.n,lruEntry.p);
6159
6160                     delete lruHash[key];
6161                   }
6162
6163                   if (!(key in data)) return;
6164
6165                   delete data[key];
6166                   size--;
6167                 },
6168
6169
6170                 /**
6171                  * @ngdoc method
6172                  * @name $cacheFactory.Cache#removeAll
6173                  * @kind function
6174                  *
6175                  * @description
6176                  * Clears the cache object of any entries.
6177                  */
6178                 removeAll: function() {
6179                   data = createMap();
6180                   size = 0;
6181                   lruHash = createMap();
6182                   freshEnd = staleEnd = null;
6183                 },
6184
6185
6186                 /**
6187                  * @ngdoc method
6188                  * @name $cacheFactory.Cache#destroy
6189                  * @kind function
6190                  *
6191                  * @description
6192                  * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6193                  * removing it from the {@link $cacheFactory $cacheFactory} set.
6194                  */
6195                 destroy: function() {
6196                   data = null;
6197                   stats = null;
6198                   lruHash = null;
6199                   delete caches[cacheId];
6200                 },
6201
6202
6203                 /**
6204                  * @ngdoc method
6205                  * @name $cacheFactory.Cache#info
6206                  * @kind function
6207                  *
6208                  * @description
6209                  * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6210                  *
6211                  * @returns {object} an object with the following properties:
6212                  *   <ul>
6213                  *     <li>**id**: the id of the cache instance</li>
6214                  *     <li>**size**: the number of entries kept in the cache instance</li>
6215                  *     <li>**...**: any additional properties from the options object when creating the
6216                  *       cache.</li>
6217                  *   </ul>
6218                  */
6219                 info: function() {
6220                   return extend({}, stats, {size: size});
6221                 }
6222               };
6223
6224
6225               /**
6226                * makes the `entry` the freshEnd of the LRU linked list
6227                */
6228               function refresh(entry) {
6229                 if (entry != freshEnd) {
6230                   if (!staleEnd) {
6231                     staleEnd = entry;
6232                   } else if (staleEnd == entry) {
6233                     staleEnd = entry.n;
6234                   }
6235
6236                   link(entry.n, entry.p);
6237                   link(entry, freshEnd);
6238                   freshEnd = entry;
6239                   freshEnd.n = null;
6240                 }
6241               }
6242
6243
6244               /**
6245                * bidirectionally links two entries of the LRU linked list
6246                */
6247               function link(nextEntry, prevEntry) {
6248                 if (nextEntry != prevEntry) {
6249                   if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6250                   if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6251                 }
6252               }
6253             }
6254
6255
6256           /**
6257            * @ngdoc method
6258            * @name $cacheFactory#info
6259            *
6260            * @description
6261            * Get information about all the caches that have been created
6262            *
6263            * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6264            */
6265             cacheFactory.info = function() {
6266               var info = {};
6267               forEach(caches, function(cache, cacheId) {
6268                 info[cacheId] = cache.info();
6269               });
6270               return info;
6271             };
6272
6273
6274           /**
6275            * @ngdoc method
6276            * @name $cacheFactory#get
6277            *
6278            * @description
6279            * Get access to a cache object by the `cacheId` used when it was created.
6280            *
6281            * @param {string} cacheId Name or id of a cache to access.
6282            * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6283            */
6284             cacheFactory.get = function(cacheId) {
6285               return caches[cacheId];
6286             };
6287
6288
6289             return cacheFactory;
6290           };
6291         }
6292
6293         /**
6294          * @ngdoc service
6295          * @name $templateCache
6296          *
6297          * @description
6298          * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6299          * can load templates directly into the cache in a `script` tag, or by consuming the
6300          * `$templateCache` service directly.
6301          *
6302          * Adding via the `script` tag:
6303          *
6304          * ```html
6305          *   <script type="text/ng-template" id="templateId.html">
6306          *     <p>This is the content of the template</p>
6307          *   </script>
6308          * ```
6309          *
6310          * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6311          * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6312          * element with ng-app attribute), otherwise the template will be ignored.
6313          *
6314          * Adding via the `$templateCache` service:
6315          *
6316          * ```js
6317          * var myApp = angular.module('myApp', []);
6318          * myApp.run(function($templateCache) {
6319          *   $templateCache.put('templateId.html', 'This is the content of the template');
6320          * });
6321          * ```
6322          *
6323          * To retrieve the template later, simply use it in your HTML:
6324          * ```html
6325          * <div ng-include=" 'templateId.html' "></div>
6326          * ```
6327          *
6328          * or get it via Javascript:
6329          * ```js
6330          * $templateCache.get('templateId.html')
6331          * ```
6332          *
6333          * See {@link ng.$cacheFactory $cacheFactory}.
6334          *
6335          */
6336         function $TemplateCacheProvider() {
6337           this.$get = ['$cacheFactory', function($cacheFactory) {
6338             return $cacheFactory('templates');
6339           }];
6340         }
6341
6342         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6343          *     Any commits to this file should be reviewed with security in mind.  *
6344          *   Changes to this file can potentially create security vulnerabilities. *
6345          *          An approval from 2 Core members with history of modifying      *
6346          *                         this file is required.                          *
6347          *                                                                         *
6348          *  Does the change somehow allow for arbitrary javascript to be executed? *
6349          *    Or allows for someone to change the prototype of built-in objects?   *
6350          *     Or gives undesired access to variables likes document or window?    *
6351          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6352
6353         /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6354          *
6355          * DOM-related variables:
6356          *
6357          * - "node" - DOM Node
6358          * - "element" - DOM Element or Node
6359          * - "$node" or "$element" - jqLite-wrapped node or element
6360          *
6361          *
6362          * Compiler related stuff:
6363          *
6364          * - "linkFn" - linking fn of a single directive
6365          * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6366          * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6367          * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6368          */
6369
6370
6371         /**
6372          * @ngdoc service
6373          * @name $compile
6374          * @kind function
6375          *
6376          * @description
6377          * Compiles an HTML string or DOM into a template and produces a template function, which
6378          * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6379          *
6380          * The compilation is a process of walking the DOM tree and matching DOM elements to
6381          * {@link ng.$compileProvider#directive directives}.
6382          *
6383          * <div class="alert alert-warning">
6384          * **Note:** This document is an in-depth reference of all directive options.
6385          * For a gentle introduction to directives with examples of common use cases,
6386          * see the {@link guide/directive directive guide}.
6387          * </div>
6388          *
6389          * ## Comprehensive Directive API
6390          *
6391          * There are many different options for a directive.
6392          *
6393          * The difference resides in the return value of the factory function.
6394          * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6395          * or just the `postLink` function (all other properties will have the default values).
6396          *
6397          * <div class="alert alert-success">
6398          * **Best Practice:** It's recommended to use the "directive definition object" form.
6399          * </div>
6400          *
6401          * Here's an example directive declared with a Directive Definition Object:
6402          *
6403          * ```js
6404          *   var myModule = angular.module(...);
6405          *
6406          *   myModule.directive('directiveName', function factory(injectables) {
6407          *     var directiveDefinitionObject = {
6408          *       priority: 0,
6409          *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6410          *       // or
6411          *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6412          *       transclude: false,
6413          *       restrict: 'A',
6414          *       templateNamespace: 'html',
6415          *       scope: false,
6416          *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6417          *       controllerAs: 'stringIdentifier',
6418          *       bindToController: false,
6419          *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6420          *       compile: function compile(tElement, tAttrs, transclude) {
6421          *         return {
6422          *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6423          *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6424          *         }
6425          *         // or
6426          *         // return function postLink( ... ) { ... }
6427          *       },
6428          *       // or
6429          *       // link: {
6430          *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6431          *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6432          *       // }
6433          *       // or
6434          *       // link: function postLink( ... ) { ... }
6435          *     };
6436          *     return directiveDefinitionObject;
6437          *   });
6438          * ```
6439          *
6440          * <div class="alert alert-warning">
6441          * **Note:** Any unspecified options will use the default value. You can see the default values below.
6442          * </div>
6443          *
6444          * Therefore the above can be simplified as:
6445          *
6446          * ```js
6447          *   var myModule = angular.module(...);
6448          *
6449          *   myModule.directive('directiveName', function factory(injectables) {
6450          *     var directiveDefinitionObject = {
6451          *       link: function postLink(scope, iElement, iAttrs) { ... }
6452          *     };
6453          *     return directiveDefinitionObject;
6454          *     // or
6455          *     // return function postLink(scope, iElement, iAttrs) { ... }
6456          *   });
6457          * ```
6458          *
6459          *
6460          *
6461          * ### Directive Definition Object
6462          *
6463          * The directive definition object provides instructions to the {@link ng.$compile
6464          * compiler}. The attributes are:
6465          *
6466          * #### `multiElement`
6467          * When this property is set to true, the HTML compiler will collect DOM nodes between
6468          * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6469          * together as the directive elements. It is recommended that this feature be used on directives
6470          * which are not strictly behavioural (such as {@link ngClick}), and which
6471          * do not manipulate or replace child nodes (such as {@link ngInclude}).
6472          *
6473          * #### `priority`
6474          * When there are multiple directives defined on a single DOM element, sometimes it
6475          * is necessary to specify the order in which the directives are applied. The `priority` is used
6476          * to sort the directives before their `compile` functions get called. Priority is defined as a
6477          * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6478          * are also run in priority order, but post-link functions are run in reverse order. The order
6479          * of directives with the same priority is undefined. The default priority is `0`.
6480          *
6481          * #### `terminal`
6482          * If set to true then the current `priority` will be the last set of directives
6483          * which will execute (any directives at the current priority will still execute
6484          * as the order of execution on same `priority` is undefined). Note that expressions
6485          * and other directives used in the directive's template will also be excluded from execution.
6486          *
6487          * #### `scope`
6488          * The scope property can be `true`, an object or a falsy value:
6489          *
6490          * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6491          *
6492          * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6493          * the directive's element. If multiple directives on the same element request a new scope,
6494          * only one new scope is created. The new scope rule does not apply for the root of the template
6495          * since the root of the template always gets a new scope.
6496          *
6497          * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6498          * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6499          * scope. This is useful when creating reusable components, which should not accidentally read or modify
6500          * data in the parent scope.
6501          *
6502          * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6503          * directive's element. These local properties are useful for aliasing values for templates. The keys in
6504          * the object hash map to the name of the property on the isolate scope; the values define how the property
6505          * is bound to the parent scope, via matching attributes on the directive's element:
6506          *
6507          * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6508          *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6509          *   attribute name is assumed to be the same as the local name.
6510          *   Given `<widget my-attr="hello {{name}}">` and widget definition
6511          *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6512          *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6513          *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6514          *   component scope).
6515          *
6516          * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6517          *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6518          *   name is specified then the attribute name is assumed to be the same as the local name.
6519          *   Given `<widget my-attr="parentModel">` and widget definition of
6520          *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6521          *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6522          *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6523          *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6524          *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6525          *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6526          *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6527          *
6528          * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6529          *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6530          *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6531          *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6532          *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6533          *   pass data from the isolated scope via an expression to the parent scope, this can be
6534          *   done by passing a map of local variable names and values into the expression wrapper fn.
6535          *   For example, if the expression is `increment(amount)` then we can specify the amount value
6536          *   by calling the `localFn` as `localFn({amount: 22})`.
6537          *
6538          * In general it's possible to apply more than one directive to one element, but there might be limitations
6539          * depending on the type of scope required by the directives. The following points will help explain these limitations.
6540          * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6541          *
6542          * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6543          * * **child scope** + **no scope** =>  Both directives will share one single child scope
6544          * * **child scope** + **child scope** =>  Both directives will share one single child scope
6545          * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6546          * its parent's scope
6547          * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6548          * be applied to the same element.
6549          * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6550          * cannot be applied to the same element.
6551          *
6552          *
6553          * #### `bindToController`
6554          * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6555          * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6556          * is instantiated, the initial values of the isolate scope bindings are already available.
6557          *
6558          * #### `controller`
6559          * Controller constructor function. The controller is instantiated before the
6560          * pre-linking phase and can be accessed by other directives (see
6561          * `require` attribute). This allows the directives to communicate with each other and augment
6562          * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6563          *
6564          * * `$scope` - Current scope associated with the element
6565          * * `$element` - Current element
6566          * * `$attrs` - Current attributes object for the element
6567          * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6568          *   `function([scope], cloneLinkingFn, futureParentElement)`.
6569          *    * `scope`: optional argument to override the scope.
6570          *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6571          *    * `futureParentElement`:
6572          *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6573          *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6574          *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6575          *          and when the `cloneLinkinFn` is passed,
6576          *          as those elements need to created and cloned in a special way when they are defined outside their
6577          *          usual containers (e.g. like `<svg>`).
6578          *        * See also the `directive.templateNamespace` property.
6579          *
6580          *
6581          * #### `require`
6582          * Require another directive and inject its controller as the fourth argument to the linking function. The
6583          * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6584          * injected argument will be an array in corresponding order. If no such directive can be
6585          * found, or if the directive does not have a controller, then an error is raised (unless no link function
6586          * is specified, in which case error checking is skipped). The name can be prefixed with:
6587          *
6588          * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6589          * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6590          * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6591          * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6592          * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6593          *   `null` to the `link` fn if not found.
6594          * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6595          *   `null` to the `link` fn if not found.
6596          *
6597          *
6598          * #### `controllerAs`
6599          * Identifier name for a reference to the controller in the directive's scope.
6600          * This allows the controller to be referenced from the directive template. This is especially
6601          * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6602          * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6603          * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6604          *
6605          *
6606          * #### `restrict`
6607          * String of subset of `EACM` which restricts the directive to a specific directive
6608          * declaration style. If omitted, the defaults (elements and attributes) are used.
6609          *
6610          * * `E` - Element name (default): `<my-directive></my-directive>`
6611          * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6612          * * `C` - Class: `<div class="my-directive: exp;"></div>`
6613          * * `M` - Comment: `<!-- directive: my-directive exp -->`
6614          *
6615          *
6616          * #### `templateNamespace`
6617          * String representing the document type used by the markup in the template.
6618          * AngularJS needs this information as those elements need to be created and cloned
6619          * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6620          *
6621          * * `html` - All root nodes in the template are HTML. Root nodes may also be
6622          *   top-level elements such as `<svg>` or `<math>`.
6623          * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6624          * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6625          *
6626          * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6627          *
6628          * #### `template`
6629          * HTML markup that may:
6630          * * Replace the contents of the directive's element (default).
6631          * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6632          * * Wrap the contents of the directive's element (if `transclude` is true).
6633          *
6634          * Value may be:
6635          *
6636          * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6637          * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6638          *   function api below) and returns a string value.
6639          *
6640          *
6641          * #### `templateUrl`
6642          * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6643          *
6644          * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6645          * for later when the template has been resolved.  In the meantime it will continue to compile and link
6646          * sibling and parent elements as though this element had not contained any directives.
6647          *
6648          * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6649          * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6650          * case when only one deeply nested directive has `templateUrl`.
6651          *
6652          * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6653          *
6654          * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6655          * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6656          * a string value representing the url.  In either case, the template URL is passed through {@link
6657          * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6658          *
6659          *
6660          * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6661          * specify what the template should replace. Defaults to `false`.
6662          *
6663          * * `true` - the template will replace the directive's element.
6664          * * `false` - the template will replace the contents of the directive's element.
6665          *
6666          * The replacement process migrates all of the attributes / classes from the old element to the new
6667          * one. See the {@link guide/directive#template-expanding-directive
6668          * Directives Guide} for an example.
6669          *
6670          * There are very few scenarios where element replacement is required for the application function,
6671          * the main one being reusable custom components that are used within SVG contexts
6672          * (because SVG doesn't work with custom elements in the DOM tree).
6673          *
6674          * #### `transclude`
6675          * Extract the contents of the element where the directive appears and make it available to the directive.
6676          * The contents are compiled and provided to the directive as a **transclusion function**. See the
6677          * {@link $compile#transclusion Transclusion} section below.
6678          *
6679          * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6680          * directive's element or the entire element:
6681          *
6682          * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6683          * * `'element'` - transclude the whole of the directive's element including any directives on this
6684          *   element that defined at a lower priority than this directive. When used, the `template`
6685          *   property is ignored.
6686          *
6687          *
6688          * #### `compile`
6689          *
6690          * ```js
6691          *   function compile(tElement, tAttrs, transclude) { ... }
6692          * ```
6693          *
6694          * The compile function deals with transforming the template DOM. Since most directives do not do
6695          * template transformation, it is not used often. The compile function takes the following arguments:
6696          *
6697          *   * `tElement` - template element - The element where the directive has been declared. It is
6698          *     safe to do template transformation on the element and child elements only.
6699          *
6700          *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6701          *     between all directive compile functions.
6702          *
6703          *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6704          *
6705          * <div class="alert alert-warning">
6706          * **Note:** The template instance and the link instance may be different objects if the template has
6707          * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6708          * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6709          * should be done in a linking function rather than in a compile function.
6710          * </div>
6711
6712          * <div class="alert alert-warning">
6713          * **Note:** The compile function cannot handle directives that recursively use themselves in their
6714          * own templates or compile functions. Compiling these directives results in an infinite loop and a
6715          * stack overflow errors.
6716          *
6717          * This can be avoided by manually using $compile in the postLink function to imperatively compile
6718          * a directive's template instead of relying on automatic template compilation via `template` or
6719          * `templateUrl` declaration or manual compilation inside the compile function.
6720          * </div>
6721          *
6722          * <div class="alert alert-danger">
6723          * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6724          *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6725          *   to the link function instead.
6726          * </div>
6727
6728          * A compile function can have a return value which can be either a function or an object.
6729          *
6730          * * returning a (post-link) function - is equivalent to registering the linking function via the
6731          *   `link` property of the config object when the compile function is empty.
6732          *
6733          * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6734          *   control when a linking function should be called during the linking phase. See info about
6735          *   pre-linking and post-linking functions below.
6736          *
6737          *
6738          * #### `link`
6739          * This property is used only if the `compile` property is not defined.
6740          *
6741          * ```js
6742          *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6743          * ```
6744          *
6745          * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6746          * executed after the template has been cloned. This is where most of the directive logic will be
6747          * put.
6748          *
6749          *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6750          *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6751          *
6752          *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6753          *     manipulate the children of the element only in `postLink` function since the children have
6754          *     already been linked.
6755          *
6756          *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6757          *     between all directive linking functions.
6758          *
6759          *   * `controller` - the directive's required controller instance(s) - Instances are shared
6760          *     among all directives, which allows the directives to use the controllers as a communication
6761          *     channel. The exact value depends on the directive's `require` property:
6762          *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6763          *       * `string`: the controller instance
6764          *       * `array`: array of controller instances
6765          *
6766          *     If a required controller cannot be found, and it is optional, the instance is `null`,
6767          *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6768          *
6769          *     Note that you can also require the directive's own controller - it will be made available like
6770          *     any other controller.
6771          *
6772          *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6773          *     This is the same as the `$transclude`
6774          *     parameter of directive controllers, see there for details.
6775          *     `function([scope], cloneLinkingFn, futureParentElement)`.
6776          *
6777          * #### Pre-linking function
6778          *
6779          * Executed before the child elements are linked. Not safe to do DOM transformation since the
6780          * compiler linking function will fail to locate the correct elements for linking.
6781          *
6782          * #### Post-linking function
6783          *
6784          * Executed after the child elements are linked.
6785          *
6786          * Note that child elements that contain `templateUrl` directives will not have been compiled
6787          * and linked since they are waiting for their template to load asynchronously and their own
6788          * compilation and linking has been suspended until that occurs.
6789          *
6790          * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6791          * for their async templates to be resolved.
6792          *
6793          *
6794          * ### Transclusion
6795          *
6796          * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6797          * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6798          * scope from where they were taken.
6799          *
6800          * Transclusion is used (often with {@link ngTransclude}) to insert the
6801          * original contents of a directive's element into a specified place in the template of the directive.
6802          * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6803          * content has access to the properties on the scope from which it was taken, even if the directive
6804          * has isolated scope.
6805          * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6806          *
6807          * This makes it possible for the widget to have private state for its template, while the transcluded
6808          * content has access to its originating scope.
6809          *
6810          * <div class="alert alert-warning">
6811          * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6812          * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6813          * Testing Transclusion Directives}.
6814          * </div>
6815          *
6816          * #### Transclusion Functions
6817          *
6818          * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6819          * function** to the directive's `link` function and `controller`. This transclusion function is a special
6820          * **linking function** that will return the compiled contents linked to a new transclusion scope.
6821          *
6822          * <div class="alert alert-info">
6823          * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6824          * ngTransclude will deal with it for us.
6825          * </div>
6826          *
6827          * If you want to manually control the insertion and removal of the transcluded content in your directive
6828          * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6829          * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6830          *
6831          * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6832          * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6833          * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6834          *
6835          * <div class="alert alert-info">
6836          * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6837          * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6838          * </div>
6839          *
6840          * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6841          * attach function**:
6842          *
6843          * ```js
6844          * var transcludedContent, transclusionScope;
6845          *
6846          * $transclude(function(clone, scope) {
6847          *   element.append(clone);
6848          *   transcludedContent = clone;
6849          *   transclusionScope = scope;
6850          * });
6851          * ```
6852          *
6853          * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6854          * associated transclusion scope:
6855          *
6856          * ```js
6857          * transcludedContent.remove();
6858          * transclusionScope.$destroy();
6859          * ```
6860          *
6861          * <div class="alert alert-info">
6862          * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6863          * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6864          * then you are also responsible for calling `$destroy` on the transclusion scope.
6865          * </div>
6866          *
6867          * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6868          * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6869          * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6870          *
6871          *
6872          * #### Transclusion Scopes
6873          *
6874          * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6875          * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6876          * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6877          * was taken.
6878          *
6879          * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6880          * like this:
6881          *
6882          * ```html
6883          * <div ng-app>
6884          *   <div isolate>
6885          *     <div transclusion>
6886          *     </div>
6887          *   </div>
6888          * </div>
6889          * ```
6890          *
6891          * The `$parent` scope hierarchy will look like this:
6892          *
6893          * ```
6894          * - $rootScope
6895          *   - isolate
6896          *     - transclusion
6897          * ```
6898          *
6899          * but the scopes will inherit prototypically from different scopes to their `$parent`.
6900          *
6901          * ```
6902          * - $rootScope
6903          *   - transclusion
6904          * - isolate
6905          * ```
6906          *
6907          *
6908          * ### Attributes
6909          *
6910          * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6911          * `link()` or `compile()` functions. It has a variety of uses.
6912          *
6913          * accessing *Normalized attribute names:*
6914          * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6915          * the attributes object allows for normalized access to
6916          *   the attributes.
6917          *
6918          * * *Directive inter-communication:* All directives share the same instance of the attributes
6919          *   object which allows the directives to use the attributes object as inter directive
6920          *   communication.
6921          *
6922          * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6923          *   allowing other directives to read the interpolated value.
6924          *
6925          * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6926          *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6927          *   the only way to easily get the actual value because during the linking phase the interpolation
6928          *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6929          *
6930          * ```js
6931          * function linkingFn(scope, elm, attrs, ctrl) {
6932          *   // get the attribute value
6933          *   console.log(attrs.ngModel);
6934          *
6935          *   // change the attribute
6936          *   attrs.$set('ngModel', 'new value');
6937          *
6938          *   // observe changes to interpolated attribute
6939          *   attrs.$observe('ngModel', function(value) {
6940          *     console.log('ngModel has changed value to ' + value);
6941          *   });
6942          * }
6943          * ```
6944          *
6945          * ## Example
6946          *
6947          * <div class="alert alert-warning">
6948          * **Note**: Typically directives are registered with `module.directive`. The example below is
6949          * to illustrate how `$compile` works.
6950          * </div>
6951          *
6952          <example module="compileExample">
6953            <file name="index.html">
6954             <script>
6955               angular.module('compileExample', [], function($compileProvider) {
6956                 // configure new 'compile' directive by passing a directive
6957                 // factory function. The factory function injects the '$compile'
6958                 $compileProvider.directive('compile', function($compile) {
6959                   // directive factory creates a link function
6960                   return function(scope, element, attrs) {
6961                     scope.$watch(
6962                       function(scope) {
6963                          // watch the 'compile' expression for changes
6964                         return scope.$eval(attrs.compile);
6965                       },
6966                       function(value) {
6967                         // when the 'compile' expression changes
6968                         // assign it into the current DOM
6969                         element.html(value);
6970
6971                         // compile the new DOM and link it to the current
6972                         // scope.
6973                         // NOTE: we only compile .childNodes so that
6974                         // we don't get into infinite loop compiling ourselves
6975                         $compile(element.contents())(scope);
6976                       }
6977                     );
6978                   };
6979                 });
6980               })
6981               .controller('GreeterController', ['$scope', function($scope) {
6982                 $scope.name = 'Angular';
6983                 $scope.html = 'Hello {{name}}';
6984               }]);
6985             </script>
6986             <div ng-controller="GreeterController">
6987               <input ng-model="name"> <br/>
6988               <textarea ng-model="html"></textarea> <br/>
6989               <div compile="html"></div>
6990             </div>
6991            </file>
6992            <file name="protractor.js" type="protractor">
6993              it('should auto compile', function() {
6994                var textarea = $('textarea');
6995                var output = $('div[compile]');
6996                // The initial state reads 'Hello Angular'.
6997                expect(output.getText()).toBe('Hello Angular');
6998                textarea.clear();
6999                textarea.sendKeys('{{name}}!');
7000                expect(output.getText()).toBe('Angular!');
7001              });
7002            </file>
7003          </example>
7004
7005          *
7006          *
7007          * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7008          * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7009          *
7010          * <div class="alert alert-danger">
7011          * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7012          *   e.g. will not use the right outer scope. Please pass the transclude function as a
7013          *   `parentBoundTranscludeFn` to the link function instead.
7014          * </div>
7015          *
7016          * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7017          *                 root element(s), not their children)
7018          * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7019          * (a DOM element/tree) to a scope. Where:
7020          *
7021          *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7022          *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7023          *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7024          *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7025          *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7026          *
7027          *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7028          *      * `scope` - is the current scope with which the linking function is working with.
7029          *
7030          *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7031          *  keys may be used to control linking behavior:
7032          *
7033          *      * `parentBoundTranscludeFn` - the transclude function made available to
7034          *        directives; if given, it will be passed through to the link functions of
7035          *        directives found in `element` during compilation.
7036          *      * `transcludeControllers` - an object hash with keys that map controller names
7037          *        to controller instances; if given, it will make the controllers
7038          *        available to directives.
7039          *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7040          *        the cloned elements; only needed for transcludes that are allowed to contain non html
7041          *        elements (e.g. SVG elements). See also the directive.controller property.
7042          *
7043          * Calling the linking function returns the element of the template. It is either the original
7044          * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7045          *
7046          * After linking the view is not updated until after a call to $digest which typically is done by
7047          * Angular automatically.
7048          *
7049          * If you need access to the bound view, there are two ways to do it:
7050          *
7051          * - If you are not asking the linking function to clone the template, create the DOM element(s)
7052          *   before you send them to the compiler and keep this reference around.
7053          *   ```js
7054          *     var element = $compile('<p>{{total}}</p>')(scope);
7055          *   ```
7056          *
7057          * - if on the other hand, you need the element to be cloned, the view reference from the original
7058          *   example would not point to the clone, but rather to the original template that was cloned. In
7059          *   this case, you can access the clone via the cloneAttachFn:
7060          *   ```js
7061          *     var templateElement = angular.element('<p>{{total}}</p>'),
7062          *         scope = ....;
7063          *
7064          *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7065          *       //attach the clone to DOM document at the right place
7066          *     });
7067          *
7068          *     //now we have reference to the cloned DOM via `clonedElement`
7069          *   ```
7070          *
7071          *
7072          * For information on how the compiler works, see the
7073          * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7074          */
7075
7076         var $compileMinErr = minErr('$compile');
7077
7078         /**
7079          * @ngdoc provider
7080          * @name $compileProvider
7081          *
7082          * @description
7083          */
7084         $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7085         function $CompileProvider($provide, $$sanitizeUriProvider) {
7086           var hasDirectives = {},
7087               Suffix = 'Directive',
7088               COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7089               CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7090               ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7091               REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7092
7093           // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7094           // The assumption is that future DOM event attribute names will begin with
7095           // 'on' and be composed of only English letters.
7096           var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7097
7098           function parseIsolateBindings(scope, directiveName, isController) {
7099             var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7100
7101             var bindings = {};
7102
7103             forEach(scope, function(definition, scopeName) {
7104               var match = definition.match(LOCAL_REGEXP);
7105
7106               if (!match) {
7107                 throw $compileMinErr('iscp',
7108                     "Invalid {3} for directive '{0}'." +
7109                     " Definition: {... {1}: '{2}' ...}",
7110                     directiveName, scopeName, definition,
7111                     (isController ? "controller bindings definition" :
7112                     "isolate scope definition"));
7113               }
7114
7115               bindings[scopeName] = {
7116                 mode: match[1][0],
7117                 collection: match[2] === '*',
7118                 optional: match[3] === '?',
7119                 attrName: match[4] || scopeName
7120               };
7121             });
7122
7123             return bindings;
7124           }
7125
7126           function parseDirectiveBindings(directive, directiveName) {
7127             var bindings = {
7128               isolateScope: null,
7129               bindToController: null
7130             };
7131             if (isObject(directive.scope)) {
7132               if (directive.bindToController === true) {
7133                 bindings.bindToController = parseIsolateBindings(directive.scope,
7134                                                                  directiveName, true);
7135                 bindings.isolateScope = {};
7136               } else {
7137                 bindings.isolateScope = parseIsolateBindings(directive.scope,
7138                                                              directiveName, false);
7139               }
7140             }
7141             if (isObject(directive.bindToController)) {
7142               bindings.bindToController =
7143                   parseIsolateBindings(directive.bindToController, directiveName, true);
7144             }
7145             if (isObject(bindings.bindToController)) {
7146               var controller = directive.controller;
7147               var controllerAs = directive.controllerAs;
7148               if (!controller) {
7149                 // There is no controller, there may or may not be a controllerAs property
7150                 throw $compileMinErr('noctrl',
7151                       "Cannot bind to controller without directive '{0}'s controller.",
7152                       directiveName);
7153               } else if (!identifierForController(controller, controllerAs)) {
7154                 // There is a controller, but no identifier or controllerAs property
7155                 throw $compileMinErr('noident',
7156                       "Cannot bind to controller without identifier for directive '{0}'.",
7157                       directiveName);
7158               }
7159             }
7160             return bindings;
7161           }
7162
7163           function assertValidDirectiveName(name) {
7164             var letter = name.charAt(0);
7165             if (!letter || letter !== lowercase(letter)) {
7166               throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7167             }
7168             if (name !== name.trim()) {
7169               throw $compileMinErr('baddir',
7170                     "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7171                     name);
7172             }
7173           }
7174
7175           /**
7176            * @ngdoc method
7177            * @name $compileProvider#directive
7178            * @kind function
7179            *
7180            * @description
7181            * Register a new directive with the compiler.
7182            *
7183            * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7184            *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7185            *    names and the values are the factories.
7186            * @param {Function|Array} directiveFactory An injectable directive factory function. See
7187            *    {@link guide/directive} for more info.
7188            * @returns {ng.$compileProvider} Self for chaining.
7189            */
7190            this.directive = function registerDirective(name, directiveFactory) {
7191             assertNotHasOwnProperty(name, 'directive');
7192             if (isString(name)) {
7193               assertValidDirectiveName(name);
7194               assertArg(directiveFactory, 'directiveFactory');
7195               if (!hasDirectives.hasOwnProperty(name)) {
7196                 hasDirectives[name] = [];
7197                 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7198                   function($injector, $exceptionHandler) {
7199                     var directives = [];
7200                     forEach(hasDirectives[name], function(directiveFactory, index) {
7201                       try {
7202                         var directive = $injector.invoke(directiveFactory);
7203                         if (isFunction(directive)) {
7204                           directive = { compile: valueFn(directive) };
7205                         } else if (!directive.compile && directive.link) {
7206                           directive.compile = valueFn(directive.link);
7207                         }
7208                         directive.priority = directive.priority || 0;
7209                         directive.index = index;
7210                         directive.name = directive.name || name;
7211                         directive.require = directive.require || (directive.controller && directive.name);
7212                         directive.restrict = directive.restrict || 'EA';
7213                         var bindings = directive.$$bindings =
7214                             parseDirectiveBindings(directive, directive.name);
7215                         if (isObject(bindings.isolateScope)) {
7216                           directive.$$isolateBindings = bindings.isolateScope;
7217                         }
7218                         directive.$$moduleName = directiveFactory.$$moduleName;
7219                         directives.push(directive);
7220                       } catch (e) {
7221                         $exceptionHandler(e);
7222                       }
7223                     });
7224                     return directives;
7225                   }]);
7226               }
7227               hasDirectives[name].push(directiveFactory);
7228             } else {
7229               forEach(name, reverseParams(registerDirective));
7230             }
7231             return this;
7232           };
7233
7234
7235           /**
7236            * @ngdoc method
7237            * @name $compileProvider#aHrefSanitizationWhitelist
7238            * @kind function
7239            *
7240            * @description
7241            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7242            * urls during a[href] sanitization.
7243            *
7244            * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7245            *
7246            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7247            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7248            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7249            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7250            *
7251            * @param {RegExp=} regexp New regexp to whitelist urls with.
7252            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7253            *    chaining otherwise.
7254            */
7255           this.aHrefSanitizationWhitelist = function(regexp) {
7256             if (isDefined(regexp)) {
7257               $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7258               return this;
7259             } else {
7260               return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7261             }
7262           };
7263
7264
7265           /**
7266            * @ngdoc method
7267            * @name $compileProvider#imgSrcSanitizationWhitelist
7268            * @kind function
7269            *
7270            * @description
7271            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7272            * urls during img[src] sanitization.
7273            *
7274            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7275            *
7276            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7277            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7278            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7279            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7280            *
7281            * @param {RegExp=} regexp New regexp to whitelist urls with.
7282            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7283            *    chaining otherwise.
7284            */
7285           this.imgSrcSanitizationWhitelist = function(regexp) {
7286             if (isDefined(regexp)) {
7287               $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7288               return this;
7289             } else {
7290               return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7291             }
7292           };
7293
7294           /**
7295            * @ngdoc method
7296            * @name  $compileProvider#debugInfoEnabled
7297            *
7298            * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7299            * current debugInfoEnabled state
7300            * @returns {*} current value if used as getter or itself (chaining) if used as setter
7301            *
7302            * @kind function
7303            *
7304            * @description
7305            * Call this method to enable/disable various debug runtime information in the compiler such as adding
7306            * binding information and a reference to the current scope on to DOM elements.
7307            * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7308            * * `ng-binding` CSS class
7309            * * `$binding` data property containing an array of the binding expressions
7310            *
7311            * You may want to disable this in production for a significant performance boost. See
7312            * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7313            *
7314            * The default value is true.
7315            */
7316           var debugInfoEnabled = true;
7317           this.debugInfoEnabled = function(enabled) {
7318             if (isDefined(enabled)) {
7319               debugInfoEnabled = enabled;
7320               return this;
7321             }
7322             return debugInfoEnabled;
7323           };
7324
7325           this.$get = [
7326                     '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7327                     '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7328             function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7329                      $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7330
7331             var Attributes = function(element, attributesToCopy) {
7332               if (attributesToCopy) {
7333                 var keys = Object.keys(attributesToCopy);
7334                 var i, l, key;
7335
7336                 for (i = 0, l = keys.length; i < l; i++) {
7337                   key = keys[i];
7338                   this[key] = attributesToCopy[key];
7339                 }
7340               } else {
7341                 this.$attr = {};
7342               }
7343
7344               this.$$element = element;
7345             };
7346
7347             Attributes.prototype = {
7348               /**
7349                * @ngdoc method
7350                * @name $compile.directive.Attributes#$normalize
7351                * @kind function
7352                *
7353                * @description
7354                * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7355                * `data-`) to its normalized, camelCase form.
7356                *
7357                * Also there is special case for Moz prefix starting with upper case letter.
7358                *
7359                * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7360                *
7361                * @param {string} name Name to normalize
7362                */
7363               $normalize: directiveNormalize,
7364
7365
7366               /**
7367                * @ngdoc method
7368                * @name $compile.directive.Attributes#$addClass
7369                * @kind function
7370                *
7371                * @description
7372                * Adds the CSS class value specified by the classVal parameter to the element. If animations
7373                * are enabled then an animation will be triggered for the class addition.
7374                *
7375                * @param {string} classVal The className value that will be added to the element
7376                */
7377               $addClass: function(classVal) {
7378                 if (classVal && classVal.length > 0) {
7379                   $animate.addClass(this.$$element, classVal);
7380                 }
7381               },
7382
7383               /**
7384                * @ngdoc method
7385                * @name $compile.directive.Attributes#$removeClass
7386                * @kind function
7387                *
7388                * @description
7389                * Removes the CSS class value specified by the classVal parameter from the element. If
7390                * animations are enabled then an animation will be triggered for the class removal.
7391                *
7392                * @param {string} classVal The className value that will be removed from the element
7393                */
7394               $removeClass: function(classVal) {
7395                 if (classVal && classVal.length > 0) {
7396                   $animate.removeClass(this.$$element, classVal);
7397                 }
7398               },
7399
7400               /**
7401                * @ngdoc method
7402                * @name $compile.directive.Attributes#$updateClass
7403                * @kind function
7404                *
7405                * @description
7406                * Adds and removes the appropriate CSS class values to the element based on the difference
7407                * between the new and old CSS class values (specified as newClasses and oldClasses).
7408                *
7409                * @param {string} newClasses The current CSS className value
7410                * @param {string} oldClasses The former CSS className value
7411                */
7412               $updateClass: function(newClasses, oldClasses) {
7413                 var toAdd = tokenDifference(newClasses, oldClasses);
7414                 if (toAdd && toAdd.length) {
7415                   $animate.addClass(this.$$element, toAdd);
7416                 }
7417
7418                 var toRemove = tokenDifference(oldClasses, newClasses);
7419                 if (toRemove && toRemove.length) {
7420                   $animate.removeClass(this.$$element, toRemove);
7421                 }
7422               },
7423
7424               /**
7425                * Set a normalized attribute on the element in a way such that all directives
7426                * can share the attribute. This function properly handles boolean attributes.
7427                * @param {string} key Normalized key. (ie ngAttribute)
7428                * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7429                * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7430                *     Defaults to true.
7431                * @param {string=} attrName Optional none normalized name. Defaults to key.
7432                */
7433               $set: function(key, value, writeAttr, attrName) {
7434                 // TODO: decide whether or not to throw an error if "class"
7435                 //is set through this function since it may cause $updateClass to
7436                 //become unstable.
7437
7438                 var node = this.$$element[0],
7439                     booleanKey = getBooleanAttrName(node, key),
7440                     aliasedKey = getAliasedAttrName(key),
7441                     observer = key,
7442                     nodeName;
7443
7444                 if (booleanKey) {
7445                   this.$$element.prop(key, value);
7446                   attrName = booleanKey;
7447                 } else if (aliasedKey) {
7448                   this[aliasedKey] = value;
7449                   observer = aliasedKey;
7450                 }
7451
7452                 this[key] = value;
7453
7454                 // translate normalized key to actual key
7455                 if (attrName) {
7456                   this.$attr[key] = attrName;
7457                 } else {
7458                   attrName = this.$attr[key];
7459                   if (!attrName) {
7460                     this.$attr[key] = attrName = snake_case(key, '-');
7461                   }
7462                 }
7463
7464                 nodeName = nodeName_(this.$$element);
7465
7466                 if ((nodeName === 'a' && key === 'href') ||
7467                     (nodeName === 'img' && key === 'src')) {
7468                   // sanitize a[href] and img[src] values
7469                   this[key] = value = $$sanitizeUri(value, key === 'src');
7470                 } else if (nodeName === 'img' && key === 'srcset') {
7471                   // sanitize img[srcset] values
7472                   var result = "";
7473
7474                   // first check if there are spaces because it's not the same pattern
7475                   var trimmedSrcset = trim(value);
7476                   //                (   999x   ,|   999w   ,|   ,|,   )
7477                   var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7478                   var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7479
7480                   // split srcset into tuple of uri and descriptor except for the last item
7481                   var rawUris = trimmedSrcset.split(pattern);
7482
7483                   // for each tuples
7484                   var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7485                   for (var i = 0; i < nbrUrisWith2parts; i++) {
7486                     var innerIdx = i * 2;
7487                     // sanitize the uri
7488                     result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7489                     // add the descriptor
7490                     result += (" " + trim(rawUris[innerIdx + 1]));
7491                   }
7492
7493                   // split the last item into uri and descriptor
7494                   var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7495
7496                   // sanitize the last uri
7497                   result += $$sanitizeUri(trim(lastTuple[0]), true);
7498
7499                   // and add the last descriptor if any
7500                   if (lastTuple.length === 2) {
7501                     result += (" " + trim(lastTuple[1]));
7502                   }
7503                   this[key] = value = result;
7504                 }
7505
7506                 if (writeAttr !== false) {
7507                   if (value === null || isUndefined(value)) {
7508                     this.$$element.removeAttr(attrName);
7509                   } else {
7510                     this.$$element.attr(attrName, value);
7511                   }
7512                 }
7513
7514                 // fire observers
7515                 var $$observers = this.$$observers;
7516                 $$observers && forEach($$observers[observer], function(fn) {
7517                   try {
7518                     fn(value);
7519                   } catch (e) {
7520                     $exceptionHandler(e);
7521                   }
7522                 });
7523               },
7524
7525
7526               /**
7527                * @ngdoc method
7528                * @name $compile.directive.Attributes#$observe
7529                * @kind function
7530                *
7531                * @description
7532                * Observes an interpolated attribute.
7533                *
7534                * The observer function will be invoked once during the next `$digest` following
7535                * compilation. The observer is then invoked whenever the interpolated value
7536                * changes.
7537                *
7538                * @param {string} key Normalized key. (ie ngAttribute) .
7539                * @param {function(interpolatedValue)} fn Function that will be called whenever
7540                         the interpolated value of the attribute changes.
7541                *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7542                * @returns {function()} Returns a deregistration function for this observer.
7543                */
7544               $observe: function(key, fn) {
7545                 var attrs = this,
7546                     $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7547                     listeners = ($$observers[key] || ($$observers[key] = []));
7548
7549                 listeners.push(fn);
7550                 $rootScope.$evalAsync(function() {
7551                   if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7552                     // no one registered attribute interpolation function, so lets call it manually
7553                     fn(attrs[key]);
7554                   }
7555                 });
7556
7557                 return function() {
7558                   arrayRemove(listeners, fn);
7559                 };
7560               }
7561             };
7562
7563
7564             function safeAddClass($element, className) {
7565               try {
7566                 $element.addClass(className);
7567               } catch (e) {
7568                 // ignore, since it means that we are trying to set class on
7569                 // SVG element, where class name is read-only.
7570               }
7571             }
7572
7573
7574             var startSymbol = $interpolate.startSymbol(),
7575                 endSymbol = $interpolate.endSymbol(),
7576                 denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7577                     ? identity
7578                     : function denormalizeTemplate(template) {
7579                       return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7580                 },
7581                 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7582             var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7583
7584             compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7585               var bindings = $element.data('$binding') || [];
7586
7587               if (isArray(binding)) {
7588                 bindings = bindings.concat(binding);
7589               } else {
7590                 bindings.push(binding);
7591               }
7592
7593               $element.data('$binding', bindings);
7594             } : noop;
7595
7596             compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7597               safeAddClass($element, 'ng-binding');
7598             } : noop;
7599
7600             compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7601               var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7602               $element.data(dataName, scope);
7603             } : noop;
7604
7605             compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7606               safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7607             } : noop;
7608
7609             return compile;
7610
7611             //================================
7612
7613             function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7614                                 previousCompileContext) {
7615               if (!($compileNodes instanceof jqLite)) {
7616                 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7617                 // modify it.
7618                 $compileNodes = jqLite($compileNodes);
7619               }
7620               // We can not compile top level text elements since text nodes can be merged and we will
7621               // not be able to attach scope data to them, so we will wrap them in <span>
7622               forEach($compileNodes, function(node, index) {
7623                 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7624                   $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7625                 }
7626               });
7627               var compositeLinkFn =
7628                       compileNodes($compileNodes, transcludeFn, $compileNodes,
7629                                    maxPriority, ignoreDirective, previousCompileContext);
7630               compile.$$addScopeClass($compileNodes);
7631               var namespace = null;
7632               return function publicLinkFn(scope, cloneConnectFn, options) {
7633                 assertArg(scope, 'scope');
7634
7635                 if (previousCompileContext && previousCompileContext.needsNewScope) {
7636                   // A parent directive did a replace and a directive on this element asked
7637                   // for transclusion, which caused us to lose a layer of element on which
7638                   // we could hold the new transclusion scope, so we will create it manually
7639                   // here.
7640                   scope = scope.$parent.$new();
7641                 }
7642
7643                 options = options || {};
7644                 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7645                   transcludeControllers = options.transcludeControllers,
7646                   futureParentElement = options.futureParentElement;
7647
7648                 // When `parentBoundTranscludeFn` is passed, it is a
7649                 // `controllersBoundTransclude` function (it was previously passed
7650                 // as `transclude` to directive.link) so we must unwrap it to get
7651                 // its `boundTranscludeFn`
7652                 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7653                   parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7654                 }
7655
7656                 if (!namespace) {
7657                   namespace = detectNamespaceForChildElements(futureParentElement);
7658                 }
7659                 var $linkNode;
7660                 if (namespace !== 'html') {
7661                   // When using a directive with replace:true and templateUrl the $compileNodes
7662                   // (or a child element inside of them)
7663                   // might change, so we need to recreate the namespace adapted compileNodes
7664                   // for call to the link function.
7665                   // Note: This will already clone the nodes...
7666                   $linkNode = jqLite(
7667                     wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7668                   );
7669                 } else if (cloneConnectFn) {
7670                   // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7671                   // and sometimes changes the structure of the DOM.
7672                   $linkNode = JQLitePrototype.clone.call($compileNodes);
7673                 } else {
7674                   $linkNode = $compileNodes;
7675                 }
7676
7677                 if (transcludeControllers) {
7678                   for (var controllerName in transcludeControllers) {
7679                     $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7680                   }
7681                 }
7682
7683                 compile.$$addScopeInfo($linkNode, scope);
7684
7685                 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7686                 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7687                 return $linkNode;
7688               };
7689             }
7690
7691             function detectNamespaceForChildElements(parentElement) {
7692               // TODO: Make this detect MathML as well...
7693               var node = parentElement && parentElement[0];
7694               if (!node) {
7695                 return 'html';
7696               } else {
7697                 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7698               }
7699             }
7700
7701             /**
7702              * Compile function matches each node in nodeList against the directives. Once all directives
7703              * for a particular node are collected their compile functions are executed. The compile
7704              * functions return values - the linking functions - are combined into a composite linking
7705              * function, which is the a linking function for the node.
7706              *
7707              * @param {NodeList} nodeList an array of nodes or NodeList to compile
7708              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7709              *        scope argument is auto-generated to the new child of the transcluded parent scope.
7710              * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7711              *        the rootElement must be set the jqLite collection of the compile root. This is
7712              *        needed so that the jqLite collection items can be replaced with widgets.
7713              * @param {number=} maxPriority Max directive priority.
7714              * @returns {Function} A composite linking function of all of the matched directives or null.
7715              */
7716             function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7717                                     previousCompileContext) {
7718               var linkFns = [],
7719                   attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7720
7721               for (var i = 0; i < nodeList.length; i++) {
7722                 attrs = new Attributes();
7723
7724                 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7725                 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7726                                                 ignoreDirective);
7727
7728                 nodeLinkFn = (directives.length)
7729                     ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7730                                               null, [], [], previousCompileContext)
7731                     : null;
7732
7733                 if (nodeLinkFn && nodeLinkFn.scope) {
7734                   compile.$$addScopeClass(attrs.$$element);
7735                 }
7736
7737                 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7738                               !(childNodes = nodeList[i].childNodes) ||
7739                               !childNodes.length)
7740                     ? null
7741                     : compileNodes(childNodes,
7742                          nodeLinkFn ? (
7743                           (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7744                              && nodeLinkFn.transclude) : transcludeFn);
7745
7746                 if (nodeLinkFn || childLinkFn) {
7747                   linkFns.push(i, nodeLinkFn, childLinkFn);
7748                   linkFnFound = true;
7749                   nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7750                 }
7751
7752                 //use the previous context only for the first element in the virtual group
7753                 previousCompileContext = null;
7754               }
7755
7756               // return a linking function if we have found anything, null otherwise
7757               return linkFnFound ? compositeLinkFn : null;
7758
7759               function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7760                 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7761                 var stableNodeList;
7762
7763
7764                 if (nodeLinkFnFound) {
7765                   // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7766                   // offsets don't get screwed up
7767                   var nodeListLength = nodeList.length;
7768                   stableNodeList = new Array(nodeListLength);
7769
7770                   // create a sparse array by only copying the elements which have a linkFn
7771                   for (i = 0; i < linkFns.length; i+=3) {
7772                     idx = linkFns[i];
7773                     stableNodeList[idx] = nodeList[idx];
7774                   }
7775                 } else {
7776                   stableNodeList = nodeList;
7777                 }
7778
7779                 for (i = 0, ii = linkFns.length; i < ii;) {
7780                   node = stableNodeList[linkFns[i++]];
7781                   nodeLinkFn = linkFns[i++];
7782                   childLinkFn = linkFns[i++];
7783
7784                   if (nodeLinkFn) {
7785                     if (nodeLinkFn.scope) {
7786                       childScope = scope.$new();
7787                       compile.$$addScopeInfo(jqLite(node), childScope);
7788                     } else {
7789                       childScope = scope;
7790                     }
7791
7792                     if (nodeLinkFn.transcludeOnThisElement) {
7793                       childBoundTranscludeFn = createBoundTranscludeFn(
7794                           scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7795
7796                     } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7797                       childBoundTranscludeFn = parentBoundTranscludeFn;
7798
7799                     } else if (!parentBoundTranscludeFn && transcludeFn) {
7800                       childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7801
7802                     } else {
7803                       childBoundTranscludeFn = null;
7804                     }
7805
7806                     nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7807
7808                   } else if (childLinkFn) {
7809                     childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7810                   }
7811                 }
7812               }
7813             }
7814
7815             function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7816
7817               var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7818
7819                 if (!transcludedScope) {
7820                   transcludedScope = scope.$new(false, containingScope);
7821                   transcludedScope.$$transcluded = true;
7822                 }
7823
7824                 return transcludeFn(transcludedScope, cloneFn, {
7825                   parentBoundTranscludeFn: previousBoundTranscludeFn,
7826                   transcludeControllers: controllers,
7827                   futureParentElement: futureParentElement
7828                 });
7829               };
7830
7831               return boundTranscludeFn;
7832             }
7833
7834             /**
7835              * Looks for directives on the given node and adds them to the directive collection which is
7836              * sorted.
7837              *
7838              * @param node Node to search.
7839              * @param directives An array to which the directives are added to. This array is sorted before
7840              *        the function returns.
7841              * @param attrs The shared attrs object which is used to populate the normalized attributes.
7842              * @param {number=} maxPriority Max directive priority.
7843              */
7844             function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7845               var nodeType = node.nodeType,
7846                   attrsMap = attrs.$attr,
7847                   match,
7848                   className;
7849
7850               switch (nodeType) {
7851                 case NODE_TYPE_ELEMENT: /* Element */
7852                   // use the node name: <directive>
7853                   addDirective(directives,
7854                       directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7855
7856                   // iterate over the attributes
7857                   for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7858                            j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7859                     var attrStartName = false;
7860                     var attrEndName = false;
7861
7862                     attr = nAttrs[j];
7863                     name = attr.name;
7864                     value = trim(attr.value);
7865
7866                     // support ngAttr attribute binding
7867                     ngAttrName = directiveNormalize(name);
7868                     if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7869                       name = name.replace(PREFIX_REGEXP, '')
7870                         .substr(8).replace(/_(.)/g, function(match, letter) {
7871                           return letter.toUpperCase();
7872                         });
7873                     }
7874
7875                     var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7876                     if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7877                       attrStartName = name;
7878                       attrEndName = name.substr(0, name.length - 5) + 'end';
7879                       name = name.substr(0, name.length - 6);
7880                     }
7881
7882                     nName = directiveNormalize(name.toLowerCase());
7883                     attrsMap[nName] = name;
7884                     if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7885                         attrs[nName] = value;
7886                         if (getBooleanAttrName(node, nName)) {
7887                           attrs[nName] = true; // presence means true
7888                         }
7889                     }
7890                     addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7891                     addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7892                                   attrEndName);
7893                   }
7894
7895                   // use class as directive
7896                   className = node.className;
7897                   if (isObject(className)) {
7898                       // Maybe SVGAnimatedString
7899                       className = className.animVal;
7900                   }
7901                   if (isString(className) && className !== '') {
7902                     while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7903                       nName = directiveNormalize(match[2]);
7904                       if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7905                         attrs[nName] = trim(match[3]);
7906                       }
7907                       className = className.substr(match.index + match[0].length);
7908                     }
7909                   }
7910                   break;
7911                 case NODE_TYPE_TEXT: /* Text Node */
7912                   if (msie === 11) {
7913                     // Workaround for #11781
7914                     while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7915                       node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7916                       node.parentNode.removeChild(node.nextSibling);
7917                     }
7918                   }
7919                   addTextInterpolateDirective(directives, node.nodeValue);
7920                   break;
7921                 case NODE_TYPE_COMMENT: /* Comment */
7922                   try {
7923                     match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7924                     if (match) {
7925                       nName = directiveNormalize(match[1]);
7926                       if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7927                         attrs[nName] = trim(match[2]);
7928                       }
7929                     }
7930                   } catch (e) {
7931                     // turns out that under some circumstances IE9 throws errors when one attempts to read
7932                     // comment's node value.
7933                     // Just ignore it and continue. (Can't seem to reproduce in test case.)
7934                   }
7935                   break;
7936               }
7937
7938               directives.sort(byPriority);
7939               return directives;
7940             }
7941
7942             /**
7943              * Given a node with an directive-start it collects all of the siblings until it finds
7944              * directive-end.
7945              * @param node
7946              * @param attrStart
7947              * @param attrEnd
7948              * @returns {*}
7949              */
7950             function groupScan(node, attrStart, attrEnd) {
7951               var nodes = [];
7952               var depth = 0;
7953               if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7954                 do {
7955                   if (!node) {
7956                     throw $compileMinErr('uterdir',
7957                               "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7958                               attrStart, attrEnd);
7959                   }
7960                   if (node.nodeType == NODE_TYPE_ELEMENT) {
7961                     if (node.hasAttribute(attrStart)) depth++;
7962                     if (node.hasAttribute(attrEnd)) depth--;
7963                   }
7964                   nodes.push(node);
7965                   node = node.nextSibling;
7966                 } while (depth > 0);
7967               } else {
7968                 nodes.push(node);
7969               }
7970
7971               return jqLite(nodes);
7972             }
7973
7974             /**
7975              * Wrapper for linking function which converts normal linking function into a grouped
7976              * linking function.
7977              * @param linkFn
7978              * @param attrStart
7979              * @param attrEnd
7980              * @returns {Function}
7981              */
7982             function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7983               return function(scope, element, attrs, controllers, transcludeFn) {
7984                 element = groupScan(element[0], attrStart, attrEnd);
7985                 return linkFn(scope, element, attrs, controllers, transcludeFn);
7986               };
7987             }
7988
7989             /**
7990              * Once the directives have been collected, their compile functions are executed. This method
7991              * is responsible for inlining directive templates as well as terminating the application
7992              * of the directives if the terminal directive has been reached.
7993              *
7994              * @param {Array} directives Array of collected directives to execute their compile function.
7995              *        this needs to be pre-sorted by priority order.
7996              * @param {Node} compileNode The raw DOM node to apply the compile functions to
7997              * @param {Object} templateAttrs The shared attribute function
7998              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7999              *                                                  scope argument is auto-generated to the new
8000              *                                                  child of the transcluded parent scope.
8001              * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8002              *                              argument has the root jqLite array so that we can replace nodes
8003              *                              on it.
8004              * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8005              *                                           compiling the transclusion.
8006              * @param {Array.<Function>} preLinkFns
8007              * @param {Array.<Function>} postLinkFns
8008              * @param {Object} previousCompileContext Context used for previous compilation of the current
8009              *                                        node
8010              * @returns {Function} linkFn
8011              */
8012             function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8013                                            jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8014                                            previousCompileContext) {
8015               previousCompileContext = previousCompileContext || {};
8016
8017               var terminalPriority = -Number.MAX_VALUE,
8018                   newScopeDirective = previousCompileContext.newScopeDirective,
8019                   controllerDirectives = previousCompileContext.controllerDirectives,
8020                   newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8021                   templateDirective = previousCompileContext.templateDirective,
8022                   nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8023                   hasTranscludeDirective = false,
8024                   hasTemplate = false,
8025                   hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8026                   $compileNode = templateAttrs.$$element = jqLite(compileNode),
8027                   directive,
8028                   directiveName,
8029                   $template,
8030                   replaceDirective = originalReplaceDirective,
8031                   childTranscludeFn = transcludeFn,
8032                   linkFn,
8033                   directiveValue;
8034
8035               // executes all directives on the current element
8036               for (var i = 0, ii = directives.length; i < ii; i++) {
8037                 directive = directives[i];
8038                 var attrStart = directive.$$start;
8039                 var attrEnd = directive.$$end;
8040
8041                 // collect multiblock sections
8042                 if (attrStart) {
8043                   $compileNode = groupScan(compileNode, attrStart, attrEnd);
8044                 }
8045                 $template = undefined;
8046
8047                 if (terminalPriority > directive.priority) {
8048                   break; // prevent further processing of directives
8049                 }
8050
8051                 if (directiveValue = directive.scope) {
8052
8053                   // skip the check for directives with async templates, we'll check the derived sync
8054                   // directive when the template arrives
8055                   if (!directive.templateUrl) {
8056                     if (isObject(directiveValue)) {
8057                       // This directive is trying to add an isolated scope.
8058                       // Check that there is no scope of any kind already
8059                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8060                                         directive, $compileNode);
8061                       newIsolateScopeDirective = directive;
8062                     } else {
8063                       // This directive is trying to add a child scope.
8064                       // Check that there is no isolated scope already
8065                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8066                                         $compileNode);
8067                     }
8068                   }
8069
8070                   newScopeDirective = newScopeDirective || directive;
8071                 }
8072
8073                 directiveName = directive.name;
8074
8075                 if (!directive.templateUrl && directive.controller) {
8076                   directiveValue = directive.controller;
8077                   controllerDirectives = controllerDirectives || createMap();
8078                   assertNoDuplicate("'" + directiveName + "' controller",
8079                       controllerDirectives[directiveName], directive, $compileNode);
8080                   controllerDirectives[directiveName] = directive;
8081                 }
8082
8083                 if (directiveValue = directive.transclude) {
8084                   hasTranscludeDirective = true;
8085
8086                   // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8087                   // This option should only be used by directives that know how to safely handle element transclusion,
8088                   // where the transcluded nodes are added or replaced after linking.
8089                   if (!directive.$$tlb) {
8090                     assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8091                     nonTlbTranscludeDirective = directive;
8092                   }
8093
8094                   if (directiveValue == 'element') {
8095                     hasElementTranscludeDirective = true;
8096                     terminalPriority = directive.priority;
8097                     $template = $compileNode;
8098                     $compileNode = templateAttrs.$$element =
8099                         jqLite(document.createComment(' ' + directiveName + ': ' +
8100                                                       templateAttrs[directiveName] + ' '));
8101                     compileNode = $compileNode[0];
8102                     replaceWith(jqCollection, sliceArgs($template), compileNode);
8103
8104                     childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8105                                                 replaceDirective && replaceDirective.name, {
8106                                                   // Don't pass in:
8107                                                   // - controllerDirectives - otherwise we'll create duplicates controllers
8108                                                   // - newIsolateScopeDirective or templateDirective - combining templates with
8109                                                   //   element transclusion doesn't make sense.
8110                                                   //
8111                                                   // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8112                                                   // on the same element more than once.
8113                                                   nonTlbTranscludeDirective: nonTlbTranscludeDirective
8114                                                 });
8115                   } else {
8116                     $template = jqLite(jqLiteClone(compileNode)).contents();
8117                     $compileNode.empty(); // clear contents
8118                     childTranscludeFn = compile($template, transcludeFn, undefined,
8119                         undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8120                   }
8121                 }
8122
8123                 if (directive.template) {
8124                   hasTemplate = true;
8125                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8126                   templateDirective = directive;
8127
8128                   directiveValue = (isFunction(directive.template))
8129                       ? directive.template($compileNode, templateAttrs)
8130                       : directive.template;
8131
8132                   directiveValue = denormalizeTemplate(directiveValue);
8133
8134                   if (directive.replace) {
8135                     replaceDirective = directive;
8136                     if (jqLiteIsTextNode(directiveValue)) {
8137                       $template = [];
8138                     } else {
8139                       $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8140                     }
8141                     compileNode = $template[0];
8142
8143                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8144                       throw $compileMinErr('tplrt',
8145                           "Template for directive '{0}' must have exactly one root element. {1}",
8146                           directiveName, '');
8147                     }
8148
8149                     replaceWith(jqCollection, $compileNode, compileNode);
8150
8151                     var newTemplateAttrs = {$attr: {}};
8152
8153                     // combine directives from the original node and from the template:
8154                     // - take the array of directives for this element
8155                     // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8156                     // - collect directives from the template and sort them by priority
8157                     // - combine directives as: processed + template + unprocessed
8158                     var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8159                     var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8160
8161                     if (newIsolateScopeDirective || newScopeDirective) {
8162                       // The original directive caused the current element to be replaced but this element
8163                       // also needs to have a new scope, so we need to tell the template directives
8164                       // that they would need to get their scope from further up, if they require transclusion
8165                       markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8166                     }
8167                     directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8168                     mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8169
8170                     ii = directives.length;
8171                   } else {
8172                     $compileNode.html(directiveValue);
8173                   }
8174                 }
8175
8176                 if (directive.templateUrl) {
8177                   hasTemplate = true;
8178                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8179                   templateDirective = directive;
8180
8181                   if (directive.replace) {
8182                     replaceDirective = directive;
8183                   }
8184
8185                   nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8186                       templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8187                         controllerDirectives: controllerDirectives,
8188                         newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8189                         newIsolateScopeDirective: newIsolateScopeDirective,
8190                         templateDirective: templateDirective,
8191                         nonTlbTranscludeDirective: nonTlbTranscludeDirective
8192                       });
8193                   ii = directives.length;
8194                 } else if (directive.compile) {
8195                   try {
8196                     linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8197                     if (isFunction(linkFn)) {
8198                       addLinkFns(null, linkFn, attrStart, attrEnd);
8199                     } else if (linkFn) {
8200                       addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8201                     }
8202                   } catch (e) {
8203                     $exceptionHandler(e, startingTag($compileNode));
8204                   }
8205                 }
8206
8207                 if (directive.terminal) {
8208                   nodeLinkFn.terminal = true;
8209                   terminalPriority = Math.max(terminalPriority, directive.priority);
8210                 }
8211
8212               }
8213
8214               nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8215               nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8216               nodeLinkFn.templateOnThisElement = hasTemplate;
8217               nodeLinkFn.transclude = childTranscludeFn;
8218
8219               previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8220
8221               // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8222               return nodeLinkFn;
8223
8224               ////////////////////
8225
8226               function addLinkFns(pre, post, attrStart, attrEnd) {
8227                 if (pre) {
8228                   if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8229                   pre.require = directive.require;
8230                   pre.directiveName = directiveName;
8231                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8232                     pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8233                   }
8234                   preLinkFns.push(pre);
8235                 }
8236                 if (post) {
8237                   if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8238                   post.require = directive.require;
8239                   post.directiveName = directiveName;
8240                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8241                     post = cloneAndAnnotateFn(post, {isolateScope: true});
8242                   }
8243                   postLinkFns.push(post);
8244                 }
8245               }
8246
8247
8248               function getControllers(directiveName, require, $element, elementControllers) {
8249                 var value;
8250
8251                 if (isString(require)) {
8252                   var match = require.match(REQUIRE_PREFIX_REGEXP);
8253                   var name = require.substring(match[0].length);
8254                   var inheritType = match[1] || match[3];
8255                   var optional = match[2] === '?';
8256
8257                   //If only parents then start at the parent element
8258                   if (inheritType === '^^') {
8259                     $element = $element.parent();
8260                   //Otherwise attempt getting the controller from elementControllers in case
8261                   //the element is transcluded (and has no data) and to avoid .data if possible
8262                   } else {
8263                     value = elementControllers && elementControllers[name];
8264                     value = value && value.instance;
8265                   }
8266
8267                   if (!value) {
8268                     var dataName = '$' + name + 'Controller';
8269                     value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8270                   }
8271
8272                   if (!value && !optional) {
8273                     throw $compileMinErr('ctreq',
8274                         "Controller '{0}', required by directive '{1}', can't be found!",
8275                         name, directiveName);
8276                   }
8277                 } else if (isArray(require)) {
8278                   value = [];
8279                   for (var i = 0, ii = require.length; i < ii; i++) {
8280                     value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8281                   }
8282                 }
8283
8284                 return value || null;
8285               }
8286
8287               function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8288                 var elementControllers = createMap();
8289                 for (var controllerKey in controllerDirectives) {
8290                   var directive = controllerDirectives[controllerKey];
8291                   var locals = {
8292                     $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8293                     $element: $element,
8294                     $attrs: attrs,
8295                     $transclude: transcludeFn
8296                   };
8297
8298                   var controller = directive.controller;
8299                   if (controller == '@') {
8300                     controller = attrs[directive.name];
8301                   }
8302
8303                   var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8304
8305                   // For directives with element transclusion the element is a comment,
8306                   // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8307                   // clean up (http://bugs.jquery.com/ticket/8335).
8308                   // Instead, we save the controllers for the element in a local hash and attach to .data
8309                   // later, once we have the actual element.
8310                   elementControllers[directive.name] = controllerInstance;
8311                   if (!hasElementTranscludeDirective) {
8312                     $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8313                   }
8314                 }
8315                 return elementControllers;
8316               }
8317
8318               function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8319                 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8320                     attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8321
8322                 if (compileNode === linkNode) {
8323                   attrs = templateAttrs;
8324                   $element = templateAttrs.$$element;
8325                 } else {
8326                   $element = jqLite(linkNode);
8327                   attrs = new Attributes($element, templateAttrs);
8328                 }
8329
8330                 controllerScope = scope;
8331                 if (newIsolateScopeDirective) {
8332                   isolateScope = scope.$new(true);
8333                 } else if (newScopeDirective) {
8334                   controllerScope = scope.$parent;
8335                 }
8336
8337                 if (boundTranscludeFn) {
8338                   // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8339                   // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8340                   transcludeFn = controllersBoundTransclude;
8341                   transcludeFn.$$boundTransclude = boundTranscludeFn;
8342                 }
8343
8344                 if (controllerDirectives) {
8345                   elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8346                 }
8347
8348                 if (newIsolateScopeDirective) {
8349                   // Initialize isolate scope bindings for new isolate scope directive.
8350                   compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8351                       templateDirective === newIsolateScopeDirective.$$originalDirective)));
8352                   compile.$$addScopeClass($element, true);
8353                   isolateScope.$$isolateBindings =
8354                       newIsolateScopeDirective.$$isolateBindings;
8355                   removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8356                                                 isolateScope.$$isolateBindings,
8357                                                 newIsolateScopeDirective);
8358                   if (removeScopeBindingWatches) {
8359                     isolateScope.$on('$destroy', removeScopeBindingWatches);
8360                   }
8361                 }
8362
8363                 // Initialize bindToController bindings
8364                 for (var name in elementControllers) {
8365                   var controllerDirective = controllerDirectives[name];
8366                   var controller = elementControllers[name];
8367                   var bindings = controllerDirective.$$bindings.bindToController;
8368
8369                   if (controller.identifier && bindings) {
8370                     removeControllerBindingWatches =
8371                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8372                   }
8373
8374                   var controllerResult = controller();
8375                   if (controllerResult !== controller.instance) {
8376                     // If the controller constructor has a return value, overwrite the instance
8377                     // from setupControllers
8378                     controller.instance = controllerResult;
8379                     $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8380                     removeControllerBindingWatches && removeControllerBindingWatches();
8381                     removeControllerBindingWatches =
8382                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8383                   }
8384                 }
8385
8386                 // PRELINKING
8387                 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8388                   linkFn = preLinkFns[i];
8389                   invokeLinkFn(linkFn,
8390                       linkFn.isolateScope ? isolateScope : scope,
8391                       $element,
8392                       attrs,
8393                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8394                       transcludeFn
8395                   );
8396                 }
8397
8398                 // RECURSION
8399                 // We only pass the isolate scope, if the isolate directive has a template,
8400                 // otherwise the child elements do not belong to the isolate directive.
8401                 var scopeToChild = scope;
8402                 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8403                   scopeToChild = isolateScope;
8404                 }
8405                 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8406
8407                 // POSTLINKING
8408                 for (i = postLinkFns.length - 1; i >= 0; i--) {
8409                   linkFn = postLinkFns[i];
8410                   invokeLinkFn(linkFn,
8411                       linkFn.isolateScope ? isolateScope : scope,
8412                       $element,
8413                       attrs,
8414                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8415                       transcludeFn
8416                   );
8417                 }
8418
8419                 // This is the function that is injected as `$transclude`.
8420                 // Note: all arguments are optional!
8421                 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8422                   var transcludeControllers;
8423
8424                   // No scope passed in:
8425                   if (!isScope(scope)) {
8426                     futureParentElement = cloneAttachFn;
8427                     cloneAttachFn = scope;
8428                     scope = undefined;
8429                   }
8430
8431                   if (hasElementTranscludeDirective) {
8432                     transcludeControllers = elementControllers;
8433                   }
8434                   if (!futureParentElement) {
8435                     futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8436                   }
8437                   return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8438                 }
8439               }
8440             }
8441
8442             // Depending upon the context in which a directive finds itself it might need to have a new isolated
8443             // or child scope created. For instance:
8444             // * if the directive has been pulled into a template because another directive with a higher priority
8445             // asked for element transclusion
8446             // * if the directive itself asks for transclusion but it is at the root of a template and the original
8447             // element was replaced. See https://github.com/angular/angular.js/issues/12936
8448             function markDirectiveScope(directives, isolateScope, newScope) {
8449               for (var j = 0, jj = directives.length; j < jj; j++) {
8450                 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8451               }
8452             }
8453
8454             /**
8455              * looks up the directive and decorates it with exception handling and proper parameters. We
8456              * call this the boundDirective.
8457              *
8458              * @param {string} name name of the directive to look up.
8459              * @param {string} location The directive must be found in specific format.
8460              *   String containing any of theses characters:
8461              *
8462              *   * `E`: element name
8463              *   * `A': attribute
8464              *   * `C`: class
8465              *   * `M`: comment
8466              * @returns {boolean} true if directive was added.
8467              */
8468             function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8469                                   endAttrName) {
8470               if (name === ignoreDirective) return null;
8471               var match = null;
8472               if (hasDirectives.hasOwnProperty(name)) {
8473                 for (var directive, directives = $injector.get(name + Suffix),
8474                     i = 0, ii = directives.length; i < ii; i++) {
8475                   try {
8476                     directive = directives[i];
8477                     if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8478                          directive.restrict.indexOf(location) != -1) {
8479                       if (startAttrName) {
8480                         directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8481                       }
8482                       tDirectives.push(directive);
8483                       match = directive;
8484                     }
8485                   } catch (e) { $exceptionHandler(e); }
8486                 }
8487               }
8488               return match;
8489             }
8490
8491
8492             /**
8493              * looks up the directive and returns true if it is a multi-element directive,
8494              * and therefore requires DOM nodes between -start and -end markers to be grouped
8495              * together.
8496              *
8497              * @param {string} name name of the directive to look up.
8498              * @returns true if directive was registered as multi-element.
8499              */
8500             function directiveIsMultiElement(name) {
8501               if (hasDirectives.hasOwnProperty(name)) {
8502                 for (var directive, directives = $injector.get(name + Suffix),
8503                     i = 0, ii = directives.length; i < ii; i++) {
8504                   directive = directives[i];
8505                   if (directive.multiElement) {
8506                     return true;
8507                   }
8508                 }
8509               }
8510               return false;
8511             }
8512
8513             /**
8514              * When the element is replaced with HTML template then the new attributes
8515              * on the template need to be merged with the existing attributes in the DOM.
8516              * The desired effect is to have both of the attributes present.
8517              *
8518              * @param {object} dst destination attributes (original DOM)
8519              * @param {object} src source attributes (from the directive template)
8520              */
8521             function mergeTemplateAttributes(dst, src) {
8522               var srcAttr = src.$attr,
8523                   dstAttr = dst.$attr,
8524                   $element = dst.$$element;
8525
8526               // reapply the old attributes to the new element
8527               forEach(dst, function(value, key) {
8528                 if (key.charAt(0) != '$') {
8529                   if (src[key] && src[key] !== value) {
8530                     value += (key === 'style' ? ';' : ' ') + src[key];
8531                   }
8532                   dst.$set(key, value, true, srcAttr[key]);
8533                 }
8534               });
8535
8536               // copy the new attributes on the old attrs object
8537               forEach(src, function(value, key) {
8538                 if (key == 'class') {
8539                   safeAddClass($element, value);
8540                   dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8541                 } else if (key == 'style') {
8542                   $element.attr('style', $element.attr('style') + ';' + value);
8543                   dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8544                   // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8545                   // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8546                   // have an attribute like "has-own-property" or "data-has-own-property", etc.
8547                 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8548                   dst[key] = value;
8549                   dstAttr[key] = srcAttr[key];
8550                 }
8551               });
8552             }
8553
8554
8555             function compileTemplateUrl(directives, $compileNode, tAttrs,
8556                 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8557               var linkQueue = [],
8558                   afterTemplateNodeLinkFn,
8559                   afterTemplateChildLinkFn,
8560                   beforeTemplateCompileNode = $compileNode[0],
8561                   origAsyncDirective = directives.shift(),
8562                   derivedSyncDirective = inherit(origAsyncDirective, {
8563                     templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8564                   }),
8565                   templateUrl = (isFunction(origAsyncDirective.templateUrl))
8566                       ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8567                       : origAsyncDirective.templateUrl,
8568                   templateNamespace = origAsyncDirective.templateNamespace;
8569
8570               $compileNode.empty();
8571
8572               $templateRequest(templateUrl)
8573                 .then(function(content) {
8574                   var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8575
8576                   content = denormalizeTemplate(content);
8577
8578                   if (origAsyncDirective.replace) {
8579                     if (jqLiteIsTextNode(content)) {
8580                       $template = [];
8581                     } else {
8582                       $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8583                     }
8584                     compileNode = $template[0];
8585
8586                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8587                       throw $compileMinErr('tplrt',
8588                           "Template for directive '{0}' must have exactly one root element. {1}",
8589                           origAsyncDirective.name, templateUrl);
8590                     }
8591
8592                     tempTemplateAttrs = {$attr: {}};
8593                     replaceWith($rootElement, $compileNode, compileNode);
8594                     var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8595
8596                     if (isObject(origAsyncDirective.scope)) {
8597                       // the original directive that caused the template to be loaded async required
8598                       // an isolate scope
8599                       markDirectiveScope(templateDirectives, true);
8600                     }
8601                     directives = templateDirectives.concat(directives);
8602                     mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8603                   } else {
8604                     compileNode = beforeTemplateCompileNode;
8605                     $compileNode.html(content);
8606                   }
8607
8608                   directives.unshift(derivedSyncDirective);
8609
8610                   afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8611                       childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8612                       previousCompileContext);
8613                   forEach($rootElement, function(node, i) {
8614                     if (node == compileNode) {
8615                       $rootElement[i] = $compileNode[0];
8616                     }
8617                   });
8618                   afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8619
8620                   while (linkQueue.length) {
8621                     var scope = linkQueue.shift(),
8622                         beforeTemplateLinkNode = linkQueue.shift(),
8623                         linkRootElement = linkQueue.shift(),
8624                         boundTranscludeFn = linkQueue.shift(),
8625                         linkNode = $compileNode[0];
8626
8627                     if (scope.$$destroyed) continue;
8628
8629                     if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8630                       var oldClasses = beforeTemplateLinkNode.className;
8631
8632                       if (!(previousCompileContext.hasElementTranscludeDirective &&
8633                           origAsyncDirective.replace)) {
8634                         // it was cloned therefore we have to clone as well.
8635                         linkNode = jqLiteClone(compileNode);
8636                       }
8637                       replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8638
8639                       // Copy in CSS classes from original node
8640                       safeAddClass(jqLite(linkNode), oldClasses);
8641                     }
8642                     if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8643                       childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8644                     } else {
8645                       childBoundTranscludeFn = boundTranscludeFn;
8646                     }
8647                     afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8648                       childBoundTranscludeFn);
8649                   }
8650                   linkQueue = null;
8651                 });
8652
8653               return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8654                 var childBoundTranscludeFn = boundTranscludeFn;
8655                 if (scope.$$destroyed) return;
8656                 if (linkQueue) {
8657                   linkQueue.push(scope,
8658                                  node,
8659                                  rootElement,
8660                                  childBoundTranscludeFn);
8661                 } else {
8662                   if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8663                     childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8664                   }
8665                   afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8666                 }
8667               };
8668             }
8669
8670
8671             /**
8672              * Sorting function for bound directives.
8673              */
8674             function byPriority(a, b) {
8675               var diff = b.priority - a.priority;
8676               if (diff !== 0) return diff;
8677               if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8678               return a.index - b.index;
8679             }
8680
8681             function assertNoDuplicate(what, previousDirective, directive, element) {
8682
8683               function wrapModuleNameIfDefined(moduleName) {
8684                 return moduleName ?
8685                   (' (module: ' + moduleName + ')') :
8686                   '';
8687               }
8688
8689               if (previousDirective) {
8690                 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8691                     previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8692                     directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8693               }
8694             }
8695
8696
8697             function addTextInterpolateDirective(directives, text) {
8698               var interpolateFn = $interpolate(text, true);
8699               if (interpolateFn) {
8700                 directives.push({
8701                   priority: 0,
8702                   compile: function textInterpolateCompileFn(templateNode) {
8703                     var templateNodeParent = templateNode.parent(),
8704                         hasCompileParent = !!templateNodeParent.length;
8705
8706                     // When transcluding a template that has bindings in the root
8707                     // we don't have a parent and thus need to add the class during linking fn.
8708                     if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8709
8710                     return function textInterpolateLinkFn(scope, node) {
8711                       var parent = node.parent();
8712                       if (!hasCompileParent) compile.$$addBindingClass(parent);
8713                       compile.$$addBindingInfo(parent, interpolateFn.expressions);
8714                       scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8715                         node[0].nodeValue = value;
8716                       });
8717                     };
8718                   }
8719                 });
8720               }
8721             }
8722
8723
8724             function wrapTemplate(type, template) {
8725               type = lowercase(type || 'html');
8726               switch (type) {
8727               case 'svg':
8728               case 'math':
8729                 var wrapper = document.createElement('div');
8730                 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8731                 return wrapper.childNodes[0].childNodes;
8732               default:
8733                 return template;
8734               }
8735             }
8736
8737
8738             function getTrustedContext(node, attrNormalizedName) {
8739               if (attrNormalizedName == "srcdoc") {
8740                 return $sce.HTML;
8741               }
8742               var tag = nodeName_(node);
8743               // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8744               if (attrNormalizedName == "xlinkHref" ||
8745                   (tag == "form" && attrNormalizedName == "action") ||
8746                   (tag != "img" && (attrNormalizedName == "src" ||
8747                                     attrNormalizedName == "ngSrc"))) {
8748                 return $sce.RESOURCE_URL;
8749               }
8750             }
8751
8752
8753             function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8754               var trustedContext = getTrustedContext(node, name);
8755               allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8756
8757               var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8758
8759               // no interpolation found -> ignore
8760               if (!interpolateFn) return;
8761
8762
8763               if (name === "multiple" && nodeName_(node) === "select") {
8764                 throw $compileMinErr("selmulti",
8765                     "Binding to the 'multiple' attribute is not supported. Element: {0}",
8766                     startingTag(node));
8767               }
8768
8769               directives.push({
8770                 priority: 100,
8771                 compile: function() {
8772                     return {
8773                       pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8774                         var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8775
8776                         if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8777                           throw $compileMinErr('nodomevents',
8778                               "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8779                                   "ng- versions (such as ng-click instead of onclick) instead.");
8780                         }
8781
8782                         // If the attribute has changed since last $interpolate()ed
8783                         var newValue = attr[name];
8784                         if (newValue !== value) {
8785                           // we need to interpolate again since the attribute value has been updated
8786                           // (e.g. by another directive's compile function)
8787                           // ensure unset/empty values make interpolateFn falsy
8788                           interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8789                           value = newValue;
8790                         }
8791
8792                         // if attribute was updated so that there is no interpolation going on we don't want to
8793                         // register any observers
8794                         if (!interpolateFn) return;
8795
8796                         // initialize attr object so that it's ready in case we need the value for isolate
8797                         // scope initialization, otherwise the value would not be available from isolate
8798                         // directive's linking fn during linking phase
8799                         attr[name] = interpolateFn(scope);
8800
8801                         ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8802                         (attr.$$observers && attr.$$observers[name].$$scope || scope).
8803                           $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8804                             //special case for class attribute addition + removal
8805                             //so that class changes can tap into the animation
8806                             //hooks provided by the $animate service. Be sure to
8807                             //skip animations when the first digest occurs (when
8808                             //both the new and the old values are the same) since
8809                             //the CSS classes are the non-interpolated values
8810                             if (name === 'class' && newValue != oldValue) {
8811                               attr.$updateClass(newValue, oldValue);
8812                             } else {
8813                               attr.$set(name, newValue);
8814                             }
8815                           });
8816                       }
8817                     };
8818                   }
8819               });
8820             }
8821
8822
8823             /**
8824              * This is a special jqLite.replaceWith, which can replace items which
8825              * have no parents, provided that the containing jqLite collection is provided.
8826              *
8827              * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8828              *                               in the root of the tree.
8829              * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8830              *                                  the shell, but replace its DOM node reference.
8831              * @param {Node} newNode The new DOM node.
8832              */
8833             function replaceWith($rootElement, elementsToRemove, newNode) {
8834               var firstElementToRemove = elementsToRemove[0],
8835                   removeCount = elementsToRemove.length,
8836                   parent = firstElementToRemove.parentNode,
8837                   i, ii;
8838
8839               if ($rootElement) {
8840                 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8841                   if ($rootElement[i] == firstElementToRemove) {
8842                     $rootElement[i++] = newNode;
8843                     for (var j = i, j2 = j + removeCount - 1,
8844                              jj = $rootElement.length;
8845                          j < jj; j++, j2++) {
8846                       if (j2 < jj) {
8847                         $rootElement[j] = $rootElement[j2];
8848                       } else {
8849                         delete $rootElement[j];
8850                       }
8851                     }
8852                     $rootElement.length -= removeCount - 1;
8853
8854                     // If the replaced element is also the jQuery .context then replace it
8855                     // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8856                     // http://api.jquery.com/context/
8857                     if ($rootElement.context === firstElementToRemove) {
8858                       $rootElement.context = newNode;
8859                     }
8860                     break;
8861                   }
8862                 }
8863               }
8864
8865               if (parent) {
8866                 parent.replaceChild(newNode, firstElementToRemove);
8867               }
8868
8869               // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8870               var fragment = document.createDocumentFragment();
8871               fragment.appendChild(firstElementToRemove);
8872
8873               if (jqLite.hasData(firstElementToRemove)) {
8874                 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8875                 // data here because there's no public interface in jQuery to do that and copying over
8876                 // event listeners (which is the main use of private data) wouldn't work anyway.
8877                 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8878
8879                 // Remove data of the replaced element. We cannot just call .remove()
8880                 // on the element it since that would deallocate scope that is needed
8881                 // for the new node. Instead, remove the data "manually".
8882                 if (!jQuery) {
8883                   delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8884                 } else {
8885                   // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8886                   // the replaced element. The cleanData version monkey-patched by Angular would cause
8887                   // the scope to be trashed and we do need the very same scope to work with the new
8888                   // element. However, we cannot just cache the non-patched version and use it here as
8889                   // that would break if another library patches the method after Angular does (one
8890                   // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8891                   // skipped this one time.
8892                   skipDestroyOnNextJQueryCleanData = true;
8893                   jQuery.cleanData([firstElementToRemove]);
8894                 }
8895               }
8896
8897               for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8898                 var element = elementsToRemove[k];
8899                 jqLite(element).remove(); // must do this way to clean up expando
8900                 fragment.appendChild(element);
8901                 delete elementsToRemove[k];
8902               }
8903
8904               elementsToRemove[0] = newNode;
8905               elementsToRemove.length = 1;
8906             }
8907
8908
8909             function cloneAndAnnotateFn(fn, annotation) {
8910               return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8911             }
8912
8913
8914             function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8915               try {
8916                 linkFn(scope, $element, attrs, controllers, transcludeFn);
8917               } catch (e) {
8918                 $exceptionHandler(e, startingTag($element));
8919               }
8920             }
8921
8922
8923             // Set up $watches for isolate scope and controller bindings. This process
8924             // only occurs for isolate scopes and new scopes with controllerAs.
8925             function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8926               var removeWatchCollection = [];
8927               forEach(bindings, function(definition, scopeName) {
8928                 var attrName = definition.attrName,
8929                 optional = definition.optional,
8930                 mode = definition.mode, // @, =, or &
8931                 lastValue,
8932                 parentGet, parentSet, compare;
8933
8934                 switch (mode) {
8935
8936                   case '@':
8937                     if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8938                       destination[scopeName] = attrs[attrName] = void 0;
8939                     }
8940                     attrs.$observe(attrName, function(value) {
8941                       if (isString(value)) {
8942                         destination[scopeName] = value;
8943                       }
8944                     });
8945                     attrs.$$observers[attrName].$$scope = scope;
8946                     if (isString(attrs[attrName])) {
8947                       // If the attribute has been provided then we trigger an interpolation to ensure
8948                       // the value is there for use in the link fn
8949                       destination[scopeName] = $interpolate(attrs[attrName])(scope);
8950                     }
8951                     break;
8952
8953                   case '=':
8954                     if (!hasOwnProperty.call(attrs, attrName)) {
8955                       if (optional) break;
8956                       attrs[attrName] = void 0;
8957                     }
8958                     if (optional && !attrs[attrName]) break;
8959
8960                     parentGet = $parse(attrs[attrName]);
8961                     if (parentGet.literal) {
8962                       compare = equals;
8963                     } else {
8964                       compare = function(a, b) { return a === b || (a !== a && b !== b); };
8965                     }
8966                     parentSet = parentGet.assign || function() {
8967                       // reset the change, or we will throw this exception on every $digest
8968                       lastValue = destination[scopeName] = parentGet(scope);
8969                       throw $compileMinErr('nonassign',
8970                           "Expression '{0}' used with directive '{1}' is non-assignable!",
8971                           attrs[attrName], directive.name);
8972                     };
8973                     lastValue = destination[scopeName] = parentGet(scope);
8974                     var parentValueWatch = function parentValueWatch(parentValue) {
8975                       if (!compare(parentValue, destination[scopeName])) {
8976                         // we are out of sync and need to copy
8977                         if (!compare(parentValue, lastValue)) {
8978                           // parent changed and it has precedence
8979                           destination[scopeName] = parentValue;
8980                         } else {
8981                           // if the parent can be assigned then do so
8982                           parentSet(scope, parentValue = destination[scopeName]);
8983                         }
8984                       }
8985                       return lastValue = parentValue;
8986                     };
8987                     parentValueWatch.$stateful = true;
8988                     var removeWatch;
8989                     if (definition.collection) {
8990                       removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8991                     } else {
8992                       removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8993                     }
8994                     removeWatchCollection.push(removeWatch);
8995                     break;
8996
8997                   case '&':
8998                     // Don't assign Object.prototype method to scope
8999                     parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9000
9001                     // Don't assign noop to destination if expression is not valid
9002                     if (parentGet === noop && optional) break;
9003
9004                     destination[scopeName] = function(locals) {
9005                       return parentGet(scope, locals);
9006                     };
9007                     break;
9008                 }
9009               });
9010
9011               return removeWatchCollection.length && function removeWatches() {
9012                 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9013                   removeWatchCollection[i]();
9014                 }
9015               };
9016             }
9017           }];
9018         }
9019
9020         var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9021         /**
9022          * Converts all accepted directives format into proper directive name.
9023          * @param name Name to normalize
9024          */
9025         function directiveNormalize(name) {
9026           return camelCase(name.replace(PREFIX_REGEXP, ''));
9027         }
9028
9029         /**
9030          * @ngdoc type
9031          * @name $compile.directive.Attributes
9032          *
9033          * @description
9034          * A shared object between directive compile / linking functions which contains normalized DOM
9035          * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9036          * needed since all of these are treated as equivalent in Angular:
9037          *
9038          * ```
9039          *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9040          * ```
9041          */
9042
9043         /**
9044          * @ngdoc property
9045          * @name $compile.directive.Attributes#$attr
9046          *
9047          * @description
9048          * A map of DOM element attribute names to the normalized name. This is
9049          * needed to do reverse lookup from normalized name back to actual name.
9050          */
9051
9052
9053         /**
9054          * @ngdoc method
9055          * @name $compile.directive.Attributes#$set
9056          * @kind function
9057          *
9058          * @description
9059          * Set DOM element attribute value.
9060          *
9061          *
9062          * @param {string} name Normalized element attribute name of the property to modify. The name is
9063          *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9064          *          property to the original name.
9065          * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9066          */
9067
9068
9069
9070         /**
9071          * Closure compiler type information
9072          */
9073
9074         function nodesetLinkingFn(
9075           /* angular.Scope */ scope,
9076           /* NodeList */ nodeList,
9077           /* Element */ rootElement,
9078           /* function(Function) */ boundTranscludeFn
9079         ) {}
9080
9081         function directiveLinkingFn(
9082           /* nodesetLinkingFn */ nodesetLinkingFn,
9083           /* angular.Scope */ scope,
9084           /* Node */ node,
9085           /* Element */ rootElement,
9086           /* function(Function) */ boundTranscludeFn
9087         ) {}
9088
9089         function tokenDifference(str1, str2) {
9090           var values = '',
9091               tokens1 = str1.split(/\s+/),
9092               tokens2 = str2.split(/\s+/);
9093
9094           outer:
9095           for (var i = 0; i < tokens1.length; i++) {
9096             var token = tokens1[i];
9097             for (var j = 0; j < tokens2.length; j++) {
9098               if (token == tokens2[j]) continue outer;
9099             }
9100             values += (values.length > 0 ? ' ' : '') + token;
9101           }
9102           return values;
9103         }
9104
9105         function removeComments(jqNodes) {
9106           jqNodes = jqLite(jqNodes);
9107           var i = jqNodes.length;
9108
9109           if (i <= 1) {
9110             return jqNodes;
9111           }
9112
9113           while (i--) {
9114             var node = jqNodes[i];
9115             if (node.nodeType === NODE_TYPE_COMMENT) {
9116               splice.call(jqNodes, i, 1);
9117             }
9118           }
9119           return jqNodes;
9120         }
9121
9122         var $controllerMinErr = minErr('$controller');
9123
9124
9125         var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9126         function identifierForController(controller, ident) {
9127           if (ident && isString(ident)) return ident;
9128           if (isString(controller)) {
9129             var match = CNTRL_REG.exec(controller);
9130             if (match) return match[3];
9131           }
9132         }
9133
9134
9135         /**
9136          * @ngdoc provider
9137          * @name $controllerProvider
9138          * @description
9139          * The {@link ng.$controller $controller service} is used by Angular to create new
9140          * controllers.
9141          *
9142          * This provider allows controller registration via the
9143          * {@link ng.$controllerProvider#register register} method.
9144          */
9145         function $ControllerProvider() {
9146           var controllers = {},
9147               globals = false;
9148
9149           /**
9150            * @ngdoc method
9151            * @name $controllerProvider#register
9152            * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9153            *    the names and the values are the constructors.
9154            * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9155            *    annotations in the array notation).
9156            */
9157           this.register = function(name, constructor) {
9158             assertNotHasOwnProperty(name, 'controller');
9159             if (isObject(name)) {
9160               extend(controllers, name);
9161             } else {
9162               controllers[name] = constructor;
9163             }
9164           };
9165
9166           /**
9167            * @ngdoc method
9168            * @name $controllerProvider#allowGlobals
9169            * @description If called, allows `$controller` to find controller constructors on `window`
9170            */
9171           this.allowGlobals = function() {
9172             globals = true;
9173           };
9174
9175
9176           this.$get = ['$injector', '$window', function($injector, $window) {
9177
9178             /**
9179              * @ngdoc service
9180              * @name $controller
9181              * @requires $injector
9182              *
9183              * @param {Function|string} constructor If called with a function then it's considered to be the
9184              *    controller constructor function. Otherwise it's considered to be a string which is used
9185              *    to retrieve the controller constructor using the following steps:
9186              *
9187              *    * check if a controller with given name is registered via `$controllerProvider`
9188              *    * check if evaluating the string on the current scope returns a constructor
9189              *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9190              *      `window` object (not recommended)
9191              *
9192              *    The string can use the `controller as property` syntax, where the controller instance is published
9193              *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9194              *    to work correctly.
9195              *
9196              * @param {Object} locals Injection locals for Controller.
9197              * @return {Object} Instance of given controller.
9198              *
9199              * @description
9200              * `$controller` service is responsible for instantiating controllers.
9201              *
9202              * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9203              * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9204              */
9205             return function(expression, locals, later, ident) {
9206               // PRIVATE API:
9207               //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9208               //                     If true, $controller will allocate the object with the correct
9209               //                     prototype chain, but will not invoke the controller until a returned
9210               //                     callback is invoked.
9211               //   param `ident` --- An optional label which overrides the label parsed from the controller
9212               //                     expression, if any.
9213               var instance, match, constructor, identifier;
9214               later = later === true;
9215               if (ident && isString(ident)) {
9216                 identifier = ident;
9217               }
9218
9219               if (isString(expression)) {
9220                 match = expression.match(CNTRL_REG);
9221                 if (!match) {
9222                   throw $controllerMinErr('ctrlfmt',
9223                     "Badly formed controller string '{0}'. " +
9224                     "Must match `__name__ as __id__` or `__name__`.", expression);
9225                 }
9226                 constructor = match[1],
9227                 identifier = identifier || match[3];
9228                 expression = controllers.hasOwnProperty(constructor)
9229                     ? controllers[constructor]
9230                     : getter(locals.$scope, constructor, true) ||
9231                         (globals ? getter($window, constructor, true) : undefined);
9232
9233                 assertArgFn(expression, constructor, true);
9234               }
9235
9236               if (later) {
9237                 // Instantiate controller later:
9238                 // This machinery is used to create an instance of the object before calling the
9239                 // controller's constructor itself.
9240                 //
9241                 // This allows properties to be added to the controller before the constructor is
9242                 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9243                 //
9244                 // This feature is not intended for use by applications, and is thus not documented
9245                 // publicly.
9246                 // Object creation: http://jsperf.com/create-constructor/2
9247                 var controllerPrototype = (isArray(expression) ?
9248                   expression[expression.length - 1] : expression).prototype;
9249                 instance = Object.create(controllerPrototype || null);
9250
9251                 if (identifier) {
9252                   addIdentifier(locals, identifier, instance, constructor || expression.name);
9253                 }
9254
9255                 var instantiate;
9256                 return instantiate = extend(function() {
9257                   var result = $injector.invoke(expression, instance, locals, constructor);
9258                   if (result !== instance && (isObject(result) || isFunction(result))) {
9259                     instance = result;
9260                     if (identifier) {
9261                       // If result changed, re-assign controllerAs value to scope.
9262                       addIdentifier(locals, identifier, instance, constructor || expression.name);
9263                     }
9264                   }
9265                   return instance;
9266                 }, {
9267                   instance: instance,
9268                   identifier: identifier
9269                 });
9270               }
9271
9272               instance = $injector.instantiate(expression, locals, constructor);
9273
9274               if (identifier) {
9275                 addIdentifier(locals, identifier, instance, constructor || expression.name);
9276               }
9277
9278               return instance;
9279             };
9280
9281             function addIdentifier(locals, identifier, instance, name) {
9282               if (!(locals && isObject(locals.$scope))) {
9283                 throw minErr('$controller')('noscp',
9284                   "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9285                   name, identifier);
9286               }
9287
9288               locals.$scope[identifier] = instance;
9289             }
9290           }];
9291         }
9292
9293         /**
9294          * @ngdoc service
9295          * @name $document
9296          * @requires $window
9297          *
9298          * @description
9299          * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9300          *
9301          * @example
9302            <example module="documentExample">
9303              <file name="index.html">
9304                <div ng-controller="ExampleController">
9305                  <p>$document title: <b ng-bind="title"></b></p>
9306                  <p>window.document title: <b ng-bind="windowTitle"></b></p>
9307                </div>
9308              </file>
9309              <file name="script.js">
9310                angular.module('documentExample', [])
9311                  .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9312                    $scope.title = $document[0].title;
9313                    $scope.windowTitle = angular.element(window.document)[0].title;
9314                  }]);
9315              </file>
9316            </example>
9317          */
9318         function $DocumentProvider() {
9319           this.$get = ['$window', function(window) {
9320             return jqLite(window.document);
9321           }];
9322         }
9323
9324         /**
9325          * @ngdoc service
9326          * @name $exceptionHandler
9327          * @requires ng.$log
9328          *
9329          * @description
9330          * Any uncaught exception in angular expressions is delegated to this service.
9331          * The default implementation simply delegates to `$log.error` which logs it into
9332          * the browser console.
9333          *
9334          * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9335          * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9336          *
9337          * ## Example:
9338          *
9339          * ```js
9340          *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9341          *     return function(exception, cause) {
9342          *       exception.message += ' (caused by "' + cause + '")';
9343          *       throw exception;
9344          *     };
9345          *   });
9346          * ```
9347          *
9348          * This example will override the normal action of `$exceptionHandler`, to make angular
9349          * exceptions fail hard when they happen, instead of just logging to the console.
9350          *
9351          * <hr />
9352          * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9353          * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9354          * (unless executed during a digest).
9355          *
9356          * If you wish, you can manually delegate exceptions, e.g.
9357          * `try { ... } catch(e) { $exceptionHandler(e); }`
9358          *
9359          * @param {Error} exception Exception associated with the error.
9360          * @param {string=} cause optional information about the context in which
9361          *       the error was thrown.
9362          *
9363          */
9364         function $ExceptionHandlerProvider() {
9365           this.$get = ['$log', function($log) {
9366             return function(exception, cause) {
9367               $log.error.apply($log, arguments);
9368             };
9369           }];
9370         }
9371
9372         var $$ForceReflowProvider = function() {
9373           this.$get = ['$document', function($document) {
9374             return function(domNode) {
9375               //the line below will force the browser to perform a repaint so
9376               //that all the animated elements within the animation frame will
9377               //be properly updated and drawn on screen. This is required to
9378               //ensure that the preparation animation is properly flushed so that
9379               //the active state picks up from there. DO NOT REMOVE THIS LINE.
9380               //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9381               //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9382               //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9383               if (domNode) {
9384                 if (!domNode.nodeType && domNode instanceof jqLite) {
9385                   domNode = domNode[0];
9386                 }
9387               } else {
9388                 domNode = $document[0].body;
9389               }
9390               return domNode.offsetWidth + 1;
9391             };
9392           }];
9393         };
9394
9395         var APPLICATION_JSON = 'application/json';
9396         var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9397         var JSON_START = /^\[|^\{(?!\{)/;
9398         var JSON_ENDS = {
9399           '[': /]$/,
9400           '{': /}$/
9401         };
9402         var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9403         var $httpMinErr = minErr('$http');
9404         var $httpMinErrLegacyFn = function(method) {
9405           return function() {
9406             throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9407           };
9408         };
9409
9410         function serializeValue(v) {
9411           if (isObject(v)) {
9412             return isDate(v) ? v.toISOString() : toJson(v);
9413           }
9414           return v;
9415         }
9416
9417
9418         function $HttpParamSerializerProvider() {
9419           /**
9420            * @ngdoc service
9421            * @name $httpParamSerializer
9422            * @description
9423            *
9424            * Default {@link $http `$http`} params serializer that converts objects to strings
9425            * according to the following rules:
9426            *
9427            * * `{'foo': 'bar'}` results in `foo=bar`
9428            * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9429            * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9430            * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9431            *
9432            * Note that serializer will sort the request parameters alphabetically.
9433            * */
9434
9435           this.$get = function() {
9436             return function ngParamSerializer(params) {
9437               if (!params) return '';
9438               var parts = [];
9439               forEachSorted(params, function(value, key) {
9440                 if (value === null || isUndefined(value)) return;
9441                 if (isArray(value)) {
9442                   forEach(value, function(v, k) {
9443                     parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9444                   });
9445                 } else {
9446                   parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9447                 }
9448               });
9449
9450               return parts.join('&');
9451             };
9452           };
9453         }
9454
9455         function $HttpParamSerializerJQLikeProvider() {
9456           /**
9457            * @ngdoc service
9458            * @name $httpParamSerializerJQLike
9459            * @description
9460            *
9461            * Alternative {@link $http `$http`} params serializer that follows
9462            * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9463            * The serializer will also sort the params alphabetically.
9464            *
9465            * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9466            *
9467            * ```js
9468            * $http({
9469            *   url: myUrl,
9470            *   method: 'GET',
9471            *   params: myParams,
9472            *   paramSerializer: '$httpParamSerializerJQLike'
9473            * });
9474            * ```
9475            *
9476            * It is also possible to set it as the default `paramSerializer` in the
9477            * {@link $httpProvider#defaults `$httpProvider`}.
9478            *
9479            * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9480            * form data for submission:
9481            *
9482            * ```js
9483            * .controller(function($http, $httpParamSerializerJQLike) {
9484            *   //...
9485            *
9486            *   $http({
9487            *     url: myUrl,
9488            *     method: 'POST',
9489            *     data: $httpParamSerializerJQLike(myData),
9490            *     headers: {
9491            *       'Content-Type': 'application/x-www-form-urlencoded'
9492            *     }
9493            *   });
9494            *
9495            * });
9496            * ```
9497            *
9498            * */
9499           this.$get = function() {
9500             return function jQueryLikeParamSerializer(params) {
9501               if (!params) return '';
9502               var parts = [];
9503               serialize(params, '', true);
9504               return parts.join('&');
9505
9506               function serialize(toSerialize, prefix, topLevel) {
9507                 if (toSerialize === null || isUndefined(toSerialize)) return;
9508                 if (isArray(toSerialize)) {
9509                   forEach(toSerialize, function(value, index) {
9510                     serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9511                   });
9512                 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9513                   forEachSorted(toSerialize, function(value, key) {
9514                     serialize(value, prefix +
9515                         (topLevel ? '' : '[') +
9516                         key +
9517                         (topLevel ? '' : ']'));
9518                   });
9519                 } else {
9520                   parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9521                 }
9522               }
9523             };
9524           };
9525         }
9526
9527         function defaultHttpResponseTransform(data, headers) {
9528           if (isString(data)) {
9529             // Strip json vulnerability protection prefix and trim whitespace
9530             var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9531
9532             if (tempData) {
9533               var contentType = headers('Content-Type');
9534               if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9535                 data = fromJson(tempData);
9536               }
9537             }
9538           }
9539
9540           return data;
9541         }
9542
9543         function isJsonLike(str) {
9544             var jsonStart = str.match(JSON_START);
9545             return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9546         }
9547
9548         /**
9549          * Parse headers into key value object
9550          *
9551          * @param {string} headers Raw headers as a string
9552          * @returns {Object} Parsed headers as key value object
9553          */
9554         function parseHeaders(headers) {
9555           var parsed = createMap(), i;
9556
9557           function fillInParsed(key, val) {
9558             if (key) {
9559               parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9560             }
9561           }
9562
9563           if (isString(headers)) {
9564             forEach(headers.split('\n'), function(line) {
9565               i = line.indexOf(':');
9566               fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9567             });
9568           } else if (isObject(headers)) {
9569             forEach(headers, function(headerVal, headerKey) {
9570               fillInParsed(lowercase(headerKey), trim(headerVal));
9571             });
9572           }
9573
9574           return parsed;
9575         }
9576
9577
9578         /**
9579          * Returns a function that provides access to parsed headers.
9580          *
9581          * Headers are lazy parsed when first requested.
9582          * @see parseHeaders
9583          *
9584          * @param {(string|Object)} headers Headers to provide access to.
9585          * @returns {function(string=)} Returns a getter function which if called with:
9586          *
9587          *   - if called with single an argument returns a single header value or null
9588          *   - if called with no arguments returns an object containing all headers.
9589          */
9590         function headersGetter(headers) {
9591           var headersObj;
9592
9593           return function(name) {
9594             if (!headersObj) headersObj =  parseHeaders(headers);
9595
9596             if (name) {
9597               var value = headersObj[lowercase(name)];
9598               if (value === void 0) {
9599                 value = null;
9600               }
9601               return value;
9602             }
9603
9604             return headersObj;
9605           };
9606         }
9607
9608
9609         /**
9610          * Chain all given functions
9611          *
9612          * This function is used for both request and response transforming
9613          *
9614          * @param {*} data Data to transform.
9615          * @param {function(string=)} headers HTTP headers getter fn.
9616          * @param {number} status HTTP status code of the response.
9617          * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9618          * @returns {*} Transformed data.
9619          */
9620         function transformData(data, headers, status, fns) {
9621           if (isFunction(fns)) {
9622             return fns(data, headers, status);
9623           }
9624
9625           forEach(fns, function(fn) {
9626             data = fn(data, headers, status);
9627           });
9628
9629           return data;
9630         }
9631
9632
9633         function isSuccess(status) {
9634           return 200 <= status && status < 300;
9635         }
9636
9637
9638         /**
9639          * @ngdoc provider
9640          * @name $httpProvider
9641          * @description
9642          * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9643          * */
9644         function $HttpProvider() {
9645           /**
9646            * @ngdoc property
9647            * @name $httpProvider#defaults
9648            * @description
9649            *
9650            * Object containing default values for all {@link ng.$http $http} requests.
9651            *
9652            * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9653            * that will provide the cache for all requests who set their `cache` property to `true`.
9654            * If you set the `defaults.cache = false` then only requests that specify their own custom
9655            * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9656            *
9657            * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9658            * Defaults value is `'XSRF-TOKEN'`.
9659            *
9660            * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9661            * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9662            *
9663            * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9664            * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9665            * setting default headers.
9666            *     - **`defaults.headers.common`**
9667            *     - **`defaults.headers.post`**
9668            *     - **`defaults.headers.put`**
9669            *     - **`defaults.headers.patch`**
9670            *
9671            *
9672            * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9673            *  used to the prepare string representation of request parameters (specified as an object).
9674            *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9675            *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9676            *
9677            **/
9678           var defaults = this.defaults = {
9679             // transform incoming response data
9680             transformResponse: [defaultHttpResponseTransform],
9681
9682             // transform outgoing request data
9683             transformRequest: [function(d) {
9684               return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9685             }],
9686
9687             // default headers
9688             headers: {
9689               common: {
9690                 'Accept': 'application/json, text/plain, */*'
9691               },
9692               post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9693               put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9694               patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9695             },
9696
9697             xsrfCookieName: 'XSRF-TOKEN',
9698             xsrfHeaderName: 'X-XSRF-TOKEN',
9699
9700             paramSerializer: '$httpParamSerializer'
9701           };
9702
9703           var useApplyAsync = false;
9704           /**
9705            * @ngdoc method
9706            * @name $httpProvider#useApplyAsync
9707            * @description
9708            *
9709            * Configure $http service to combine processing of multiple http responses received at around
9710            * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9711            * significant performance improvement for bigger applications that make many HTTP requests
9712            * concurrently (common during application bootstrap).
9713            *
9714            * Defaults to false. If no value is specified, returns the current configured value.
9715            *
9716            * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9717            *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9718            *    to load and share the same digest cycle.
9719            *
9720            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9721            *    otherwise, returns the current configured value.
9722            **/
9723           this.useApplyAsync = function(value) {
9724             if (isDefined(value)) {
9725               useApplyAsync = !!value;
9726               return this;
9727             }
9728             return useApplyAsync;
9729           };
9730
9731           var useLegacyPromise = true;
9732           /**
9733            * @ngdoc method
9734            * @name $httpProvider#useLegacyPromiseExtensions
9735            * @description
9736            *
9737            * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9738            * This should be used to make sure that applications work without these methods.
9739            *
9740            * Defaults to true. If no value is specified, returns the current configured value.
9741            *
9742            * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9743            *
9744            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9745            *    otherwise, returns the current configured value.
9746            **/
9747           this.useLegacyPromiseExtensions = function(value) {
9748             if (isDefined(value)) {
9749               useLegacyPromise = !!value;
9750               return this;
9751             }
9752             return useLegacyPromise;
9753           };
9754
9755           /**
9756            * @ngdoc property
9757            * @name $httpProvider#interceptors
9758            * @description
9759            *
9760            * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9761            * pre-processing of request or postprocessing of responses.
9762            *
9763            * These service factories are ordered by request, i.e. they are applied in the same order as the
9764            * array, on request, but reverse order, on response.
9765            *
9766            * {@link ng.$http#interceptors Interceptors detailed info}
9767            **/
9768           var interceptorFactories = this.interceptors = [];
9769
9770           this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9771               function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9772
9773             var defaultCache = $cacheFactory('$http');
9774
9775             /**
9776              * Make sure that default param serializer is exposed as a function
9777              */
9778             defaults.paramSerializer = isString(defaults.paramSerializer) ?
9779               $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9780
9781             /**
9782              * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9783              * The reversal is needed so that we can build up the interception chain around the
9784              * server request.
9785              */
9786             var reversedInterceptors = [];
9787
9788             forEach(interceptorFactories, function(interceptorFactory) {
9789               reversedInterceptors.unshift(isString(interceptorFactory)
9790                   ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9791             });
9792
9793             /**
9794              * @ngdoc service
9795              * @kind function
9796              * @name $http
9797              * @requires ng.$httpBackend
9798              * @requires $cacheFactory
9799              * @requires $rootScope
9800              * @requires $q
9801              * @requires $injector
9802              *
9803              * @description
9804              * The `$http` service is a core Angular service that facilitates communication with the remote
9805              * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9806              * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9807              *
9808              * For unit testing applications that use `$http` service, see
9809              * {@link ngMock.$httpBackend $httpBackend mock}.
9810              *
9811              * For a higher level of abstraction, please check out the {@link ngResource.$resource
9812              * $resource} service.
9813              *
9814              * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9815              * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9816              * it is important to familiarize yourself with these APIs and the guarantees they provide.
9817              *
9818              *
9819              * ## General usage
9820              * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9821              * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9822              *
9823              * ```js
9824              *   // Simple GET request example:
9825              *   $http({
9826              *     method: 'GET',
9827              *     url: '/someUrl'
9828              *   }).then(function successCallback(response) {
9829              *       // this callback will be called asynchronously
9830              *       // when the response is available
9831              *     }, function errorCallback(response) {
9832              *       // called asynchronously if an error occurs
9833              *       // or server returns response with an error status.
9834              *     });
9835              * ```
9836              *
9837              * The response object has these properties:
9838              *
9839              *   - **data** – `{string|Object}` – The response body transformed with the transform
9840              *     functions.
9841              *   - **status** – `{number}` – HTTP status code of the response.
9842              *   - **headers** – `{function([headerName])}` – Header getter function.
9843              *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9844              *   - **statusText** – `{string}` – HTTP status text of the response.
9845              *
9846              * A response status code between 200 and 299 is considered a success status and
9847              * will result in the success callback being called. Note that if the response is a redirect,
9848              * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9849              * called for such responses.
9850              *
9851              *
9852              * ## Shortcut methods
9853              *
9854              * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9855              * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9856              * last argument.
9857              *
9858              * ```js
9859              *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9860              *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9861              * ```
9862              *
9863              * Complete list of shortcut methods:
9864              *
9865              * - {@link ng.$http#get $http.get}
9866              * - {@link ng.$http#head $http.head}
9867              * - {@link ng.$http#post $http.post}
9868              * - {@link ng.$http#put $http.put}
9869              * - {@link ng.$http#delete $http.delete}
9870              * - {@link ng.$http#jsonp $http.jsonp}
9871              * - {@link ng.$http#patch $http.patch}
9872              *
9873              *
9874              * ## Writing Unit Tests that use $http
9875              * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9876              * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9877              * request using trained responses.
9878              *
9879              * ```
9880              * $httpBackend.expectGET(...);
9881              * $http.get(...);
9882              * $httpBackend.flush();
9883              * ```
9884              *
9885              * ## Deprecation Notice
9886              * <div class="alert alert-danger">
9887              *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9888              *   Use the standard `then` method instead.
9889              *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9890              *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9891              * </div>
9892              *
9893              * ## Setting HTTP Headers
9894              *
9895              * The $http service will automatically add certain HTTP headers to all requests. These defaults
9896              * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9897              * object, which currently contains this default configuration:
9898              *
9899              * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9900              *   - `Accept: application/json, text/plain, * / *`
9901              * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9902              *   - `Content-Type: application/json`
9903              * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9904              *   - `Content-Type: application/json`
9905              *
9906              * To add or overwrite these defaults, simply add or remove a property from these configuration
9907              * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9908              * with the lowercased HTTP method name as the key, e.g.
9909              * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9910              *
9911              * The defaults can also be set at runtime via the `$http.defaults` object in the same
9912              * fashion. For example:
9913              *
9914              * ```
9915              * module.run(function($http) {
9916              *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9917              * });
9918              * ```
9919              *
9920              * In addition, you can supply a `headers` property in the config object passed when
9921              * calling `$http(config)`, which overrides the defaults without changing them globally.
9922              *
9923              * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9924              * Use the `headers` property, setting the desired header to `undefined`. For example:
9925              *
9926              * ```js
9927              * var req = {
9928              *  method: 'POST',
9929              *  url: 'http://example.com',
9930              *  headers: {
9931              *    'Content-Type': undefined
9932              *  },
9933              *  data: { test: 'test' }
9934              * }
9935              *
9936              * $http(req).then(function(){...}, function(){...});
9937              * ```
9938              *
9939              * ## Transforming Requests and Responses
9940              *
9941              * Both requests and responses can be transformed using transformation functions: `transformRequest`
9942              * and `transformResponse`. These properties can be a single function that returns
9943              * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9944              * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9945              *
9946              * ### Default Transformations
9947              *
9948              * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9949              * `defaults.transformResponse` properties. If a request does not provide its own transformations
9950              * then these will be applied.
9951              *
9952              * You can augment or replace the default transformations by modifying these properties by adding to or
9953              * replacing the array.
9954              *
9955              * Angular provides the following default transformations:
9956              *
9957              * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9958              *
9959              * - If the `data` property of the request configuration object contains an object, serialize it
9960              *   into JSON format.
9961              *
9962              * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9963              *
9964              *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9965              *  - If JSON response is detected, deserialize it using a JSON parser.
9966              *
9967              *
9968              * ### Overriding the Default Transformations Per Request
9969              *
9970              * If you wish override the request/response transformations only for a single request then provide
9971              * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9972              * into `$http`.
9973              *
9974              * Note that if you provide these properties on the config object the default transformations will be
9975              * overwritten. If you wish to augment the default transformations then you must include them in your
9976              * local transformation array.
9977              *
9978              * The following code demonstrates adding a new response transformation to be run after the default response
9979              * transformations have been run.
9980              *
9981              * ```js
9982              * function appendTransform(defaults, transform) {
9983              *
9984              *   // We can't guarantee that the default transformation is an array
9985              *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9986              *
9987              *   // Append the new transformation to the defaults
9988              *   return defaults.concat(transform);
9989              * }
9990              *
9991              * $http({
9992              *   url: '...',
9993              *   method: 'GET',
9994              *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9995              *     return doTransform(value);
9996              *   })
9997              * });
9998              * ```
9999              *
10000              *
10001              * ## Caching
10002              *
10003              * To enable caching, set the request configuration `cache` property to `true` (to use default
10004              * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10005              * When the cache is enabled, `$http` stores the response from the server in the specified
10006              * cache. The next time the same request is made, the response is served from the cache without
10007              * sending a request to the server.
10008              *
10009              * Note that even if the response is served from cache, delivery of the data is asynchronous in
10010              * the same way that real requests are.
10011              *
10012              * If there are multiple GET requests for the same URL that should be cached using the same
10013              * cache, but the cache is not populated yet, only one request to the server will be made and
10014              * the remaining requests will be fulfilled using the response from the first request.
10015              *
10016              * You can change the default cache to a new object (built with
10017              * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10018              * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10019              * their `cache` property to `true` will now use this cache object.
10020              *
10021              * If you set the default cache to `false` then only requests that specify their own custom
10022              * cache object will be cached.
10023              *
10024              * ## Interceptors
10025              *
10026              * Before you start creating interceptors, be sure to understand the
10027              * {@link ng.$q $q and deferred/promise APIs}.
10028              *
10029              * For purposes of global error handling, authentication, or any kind of synchronous or
10030              * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10031              * able to intercept requests before they are handed to the server and
10032              * responses before they are handed over to the application code that
10033              * initiated these requests. The interceptors leverage the {@link ng.$q
10034              * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10035              *
10036              * The interceptors are service factories that are registered with the `$httpProvider` by
10037              * adding them to the `$httpProvider.interceptors` array. The factory is called and
10038              * injected with dependencies (if specified) and returns the interceptor.
10039              *
10040              * There are two kinds of interceptors (and two kinds of rejection interceptors):
10041              *
10042              *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10043              *     modify the `config` object or create a new one. The function needs to return the `config`
10044              *     object directly, or a promise containing the `config` or a new `config` object.
10045              *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10046              *     resolved with a rejection.
10047              *   * `response`: interceptors get called with http `response` object. The function is free to
10048              *     modify the `response` object or create a new one. The function needs to return the `response`
10049              *     object directly, or as a promise containing the `response` or a new `response` object.
10050              *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10051              *     resolved with a rejection.
10052              *
10053              *
10054              * ```js
10055              *   // register the interceptor as a service
10056              *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10057              *     return {
10058              *       // optional method
10059              *       'request': function(config) {
10060              *         // do something on success
10061              *         return config;
10062              *       },
10063              *
10064              *       // optional method
10065              *      'requestError': function(rejection) {
10066              *         // do something on error
10067              *         if (canRecover(rejection)) {
10068              *           return responseOrNewPromise
10069              *         }
10070              *         return $q.reject(rejection);
10071              *       },
10072              *
10073              *
10074              *
10075              *       // optional method
10076              *       'response': function(response) {
10077              *         // do something on success
10078              *         return response;
10079              *       },
10080              *
10081              *       // optional method
10082              *      'responseError': function(rejection) {
10083              *         // do something on error
10084              *         if (canRecover(rejection)) {
10085              *           return responseOrNewPromise
10086              *         }
10087              *         return $q.reject(rejection);
10088              *       }
10089              *     };
10090              *   });
10091              *
10092              *   $httpProvider.interceptors.push('myHttpInterceptor');
10093              *
10094              *
10095              *   // alternatively, register the interceptor via an anonymous factory
10096              *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10097              *     return {
10098              *      'request': function(config) {
10099              *          // same as above
10100              *       },
10101              *
10102              *       'response': function(response) {
10103              *          // same as above
10104              *       }
10105              *     };
10106              *   });
10107              * ```
10108              *
10109              * ## Security Considerations
10110              *
10111              * When designing web applications, consider security threats from:
10112              *
10113              * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10114              * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10115              *
10116              * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10117              * pre-configured with strategies that address these issues, but for this to work backend server
10118              * cooperation is required.
10119              *
10120              * ### JSON Vulnerability Protection
10121              *
10122              * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10123              * allows third party website to turn your JSON resource URL into
10124              * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10125              * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10126              * Angular will automatically strip the prefix before processing it as JSON.
10127              *
10128              * For example if your server needs to return:
10129              * ```js
10130              * ['one','two']
10131              * ```
10132              *
10133              * which is vulnerable to attack, your server can return:
10134              * ```js
10135              * )]}',
10136              * ['one','two']
10137              * ```
10138              *
10139              * Angular will strip the prefix, before processing the JSON.
10140              *
10141              *
10142              * ### Cross Site Request Forgery (XSRF) Protection
10143              *
10144              * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10145              * an unauthorized site can gain your user's private data. Angular provides a mechanism
10146              * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10147              * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10148              * JavaScript that runs on your domain could read the cookie, your server can be assured that
10149              * the XHR came from JavaScript running on your domain. The header will not be set for
10150              * cross-domain requests.
10151              *
10152              * To take advantage of this, your server needs to set a token in a JavaScript readable session
10153              * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10154              * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10155              * that only JavaScript running on your domain could have sent the request. The token must be
10156              * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10157              * making up its own tokens). We recommend that the token is a digest of your site's
10158              * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10159              * for added security.
10160              *
10161              * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10162              * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10163              * or the per-request config object.
10164              *
10165              * In order to prevent collisions in environments where multiple Angular apps share the
10166              * same domain or subdomain, we recommend that each application uses unique cookie name.
10167              *
10168              * @param {object} config Object describing the request to be made and how it should be
10169              *    processed. The object has following properties:
10170              *
10171              *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10172              *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10173              *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10174              *      with the `paramSerializer` and appended as GET parameters.
10175              *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10176              *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10177              *      HTTP headers to send to the server. If the return value of a function is null, the
10178              *      header will not be sent. Functions accept a config object as an argument.
10179              *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10180              *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10181              *    - **transformRequest** –
10182              *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10183              *      transform function or an array of such functions. The transform function takes the http
10184              *      request body and headers and returns its transformed (typically serialized) version.
10185              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10186              *      Overriding the Default Transformations}
10187              *    - **transformResponse** –
10188              *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10189              *      transform function or an array of such functions. The transform function takes the http
10190              *      response body, headers and status and returns its transformed (typically deserialized) version.
10191              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10192              *      Overriding the Default TransformationjqLiks}
10193              *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10194              *      prepare the string representation of request parameters (specified as an object).
10195              *      If specified as string, it is interpreted as function registered with the
10196              *      {@link $injector $injector}, which means you can create your own serializer
10197              *      by registering it as a {@link auto.$provide#service service}.
10198              *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10199              *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10200              *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10201              *      GET request, otherwise if a cache instance built with
10202              *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10203              *      caching.
10204              *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10205              *      that should abort the request when resolved.
10206              *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10207              *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10208              *      for more information.
10209              *    - **responseType** - `{string}` - see
10210              *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10211              *
10212              * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10213              *                        when the request succeeds or fails.
10214              *
10215              *
10216              * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10217              *   requests. This is primarily meant to be used for debugging purposes.
10218              *
10219              *
10220              * @example
10221         <example module="httpExample">
10222         <file name="index.html">
10223           <div ng-controller="FetchController">
10224             <select ng-model="method" aria-label="Request method">
10225               <option>GET</option>
10226               <option>JSONP</option>
10227             </select>
10228             <input type="text" ng-model="url" size="80" aria-label="URL" />
10229             <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10230             <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10231             <button id="samplejsonpbtn"
10232               ng-click="updateModel('JSONP',
10233                             'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10234               Sample JSONP
10235             </button>
10236             <button id="invalidjsonpbtn"
10237               ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10238                 Invalid JSONP
10239               </button>
10240             <pre>http status code: {{status}}</pre>
10241             <pre>http response data: {{data}}</pre>
10242           </div>
10243         </file>
10244         <file name="script.js">
10245           angular.module('httpExample', [])
10246             .controller('FetchController', ['$scope', '$http', '$templateCache',
10247               function($scope, $http, $templateCache) {
10248                 $scope.method = 'GET';
10249                 $scope.url = 'http-hello.html';
10250
10251                 $scope.fetch = function() {
10252                   $scope.code = null;
10253                   $scope.response = null;
10254
10255                   $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10256                     then(function(response) {
10257                       $scope.status = response.status;
10258                       $scope.data = response.data;
10259                     }, function(response) {
10260                       $scope.data = response.data || "Request failed";
10261                       $scope.status = response.status;
10262                   });
10263                 };
10264
10265                 $scope.updateModel = function(method, url) {
10266                   $scope.method = method;
10267                   $scope.url = url;
10268                 };
10269               }]);
10270         </file>
10271         <file name="http-hello.html">
10272           Hello, $http!
10273         </file>
10274         <file name="protractor.js" type="protractor">
10275           var status = element(by.binding('status'));
10276           var data = element(by.binding('data'));
10277           var fetchBtn = element(by.id('fetchbtn'));
10278           var sampleGetBtn = element(by.id('samplegetbtn'));
10279           var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10280           var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10281
10282           it('should make an xhr GET request', function() {
10283             sampleGetBtn.click();
10284             fetchBtn.click();
10285             expect(status.getText()).toMatch('200');
10286             expect(data.getText()).toMatch(/Hello, \$http!/);
10287           });
10288
10289         // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10290         // it('should make a JSONP request to angularjs.org', function() {
10291         //   sampleJsonpBtn.click();
10292         //   fetchBtn.click();
10293         //   expect(status.getText()).toMatch('200');
10294         //   expect(data.getText()).toMatch(/Super Hero!/);
10295         // });
10296
10297           it('should make JSONP request to invalid URL and invoke the error handler',
10298               function() {
10299             invalidJsonpBtn.click();
10300             fetchBtn.click();
10301             expect(status.getText()).toMatch('0');
10302             expect(data.getText()).toMatch('Request failed');
10303           });
10304         </file>
10305         </example>
10306              */
10307             function $http(requestConfig) {
10308
10309               if (!angular.isObject(requestConfig)) {
10310                 throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10311               }
10312
10313               var config = extend({
10314                 method: 'get',
10315                 transformRequest: defaults.transformRequest,
10316                 transformResponse: defaults.transformResponse,
10317                 paramSerializer: defaults.paramSerializer
10318               }, requestConfig);
10319
10320               config.headers = mergeHeaders(requestConfig);
10321               config.method = uppercase(config.method);
10322               config.paramSerializer = isString(config.paramSerializer) ?
10323                 $injector.get(config.paramSerializer) : config.paramSerializer;
10324
10325               var serverRequest = function(config) {
10326                 var headers = config.headers;
10327                 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10328
10329                 // strip content-type if data is undefined
10330                 if (isUndefined(reqData)) {
10331                   forEach(headers, function(value, header) {
10332                     if (lowercase(header) === 'content-type') {
10333                         delete headers[header];
10334                     }
10335                   });
10336                 }
10337
10338                 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10339                   config.withCredentials = defaults.withCredentials;
10340                 }
10341
10342                 // send request
10343                 return sendReq(config, reqData).then(transformResponse, transformResponse);
10344               };
10345
10346               var chain = [serverRequest, undefined];
10347               var promise = $q.when(config);
10348
10349               // apply interceptors
10350               forEach(reversedInterceptors, function(interceptor) {
10351                 if (interceptor.request || interceptor.requestError) {
10352                   chain.unshift(interceptor.request, interceptor.requestError);
10353                 }
10354                 if (interceptor.response || interceptor.responseError) {
10355                   chain.push(interceptor.response, interceptor.responseError);
10356                 }
10357               });
10358
10359               while (chain.length) {
10360                 var thenFn = chain.shift();
10361                 var rejectFn = chain.shift();
10362
10363                 promise = promise.then(thenFn, rejectFn);
10364               }
10365
10366               if (useLegacyPromise) {
10367                 promise.success = function(fn) {
10368                   assertArgFn(fn, 'fn');
10369
10370                   promise.then(function(response) {
10371                     fn(response.data, response.status, response.headers, config);
10372                   });
10373                   return promise;
10374                 };
10375
10376                 promise.error = function(fn) {
10377                   assertArgFn(fn, 'fn');
10378
10379                   promise.then(null, function(response) {
10380                     fn(response.data, response.status, response.headers, config);
10381                   });
10382                   return promise;
10383                 };
10384               } else {
10385                 promise.success = $httpMinErrLegacyFn('success');
10386                 promise.error = $httpMinErrLegacyFn('error');
10387               }
10388
10389               return promise;
10390
10391               function transformResponse(response) {
10392                 // make a copy since the response must be cacheable
10393                 var resp = extend({}, response);
10394                 resp.data = transformData(response.data, response.headers, response.status,
10395                                           config.transformResponse);
10396                 return (isSuccess(response.status))
10397                   ? resp
10398                   : $q.reject(resp);
10399               }
10400
10401               function executeHeaderFns(headers, config) {
10402                 var headerContent, processedHeaders = {};
10403
10404                 forEach(headers, function(headerFn, header) {
10405                   if (isFunction(headerFn)) {
10406                     headerContent = headerFn(config);
10407                     if (headerContent != null) {
10408                       processedHeaders[header] = headerContent;
10409                     }
10410                   } else {
10411                     processedHeaders[header] = headerFn;
10412                   }
10413                 });
10414
10415                 return processedHeaders;
10416               }
10417
10418               function mergeHeaders(config) {
10419                 var defHeaders = defaults.headers,
10420                     reqHeaders = extend({}, config.headers),
10421                     defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10422
10423                 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10424
10425                 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10426                 defaultHeadersIteration:
10427                 for (defHeaderName in defHeaders) {
10428                   lowercaseDefHeaderName = lowercase(defHeaderName);
10429
10430                   for (reqHeaderName in reqHeaders) {
10431                     if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10432                       continue defaultHeadersIteration;
10433                     }
10434                   }
10435
10436                   reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10437                 }
10438
10439                 // execute if header value is a function for merged headers
10440                 return executeHeaderFns(reqHeaders, shallowCopy(config));
10441               }
10442             }
10443
10444             $http.pendingRequests = [];
10445
10446             /**
10447              * @ngdoc method
10448              * @name $http#get
10449              *
10450              * @description
10451              * Shortcut method to perform `GET` request.
10452              *
10453              * @param {string} url Relative or absolute URL specifying the destination of the request
10454              * @param {Object=} config Optional configuration object
10455              * @returns {HttpPromise} Future object
10456              */
10457
10458             /**
10459              * @ngdoc method
10460              * @name $http#delete
10461              *
10462              * @description
10463              * Shortcut method to perform `DELETE` request.
10464              *
10465              * @param {string} url Relative or absolute URL specifying the destination of the request
10466              * @param {Object=} config Optional configuration object
10467              * @returns {HttpPromise} Future object
10468              */
10469
10470             /**
10471              * @ngdoc method
10472              * @name $http#head
10473              *
10474              * @description
10475              * Shortcut method to perform `HEAD` request.
10476              *
10477              * @param {string} url Relative or absolute URL specifying the destination of the request
10478              * @param {Object=} config Optional configuration object
10479              * @returns {HttpPromise} Future object
10480              */
10481
10482             /**
10483              * @ngdoc method
10484              * @name $http#jsonp
10485              *
10486              * @description
10487              * Shortcut method to perform `JSONP` request.
10488              *
10489              * @param {string} url Relative or absolute URL specifying the destination of the request.
10490              *                     The name of the callback should be the string `JSON_CALLBACK`.
10491              * @param {Object=} config Optional configuration object
10492              * @returns {HttpPromise} Future object
10493              */
10494             createShortMethods('get', 'delete', 'head', 'jsonp');
10495
10496             /**
10497              * @ngdoc method
10498              * @name $http#post
10499              *
10500              * @description
10501              * Shortcut method to perform `POST` request.
10502              *
10503              * @param {string} url Relative or absolute URL specifying the destination of the request
10504              * @param {*} data Request content
10505              * @param {Object=} config Optional configuration object
10506              * @returns {HttpPromise} Future object
10507              */
10508
10509             /**
10510              * @ngdoc method
10511              * @name $http#put
10512              *
10513              * @description
10514              * Shortcut method to perform `PUT` request.
10515              *
10516              * @param {string} url Relative or absolute URL specifying the destination of the request
10517              * @param {*} data Request content
10518              * @param {Object=} config Optional configuration object
10519              * @returns {HttpPromise} Future object
10520              */
10521
10522              /**
10523               * @ngdoc method
10524               * @name $http#patch
10525               *
10526               * @description
10527               * Shortcut method to perform `PATCH` request.
10528               *
10529               * @param {string} url Relative or absolute URL specifying the destination of the request
10530               * @param {*} data Request content
10531               * @param {Object=} config Optional configuration object
10532               * @returns {HttpPromise} Future object
10533               */
10534             createShortMethodsWithData('post', 'put', 'patch');
10535
10536                 /**
10537                  * @ngdoc property
10538                  * @name $http#defaults
10539                  *
10540                  * @description
10541                  * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10542                  * default headers, withCredentials as well as request and response transformations.
10543                  *
10544                  * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10545                  */
10546             $http.defaults = defaults;
10547
10548
10549             return $http;
10550
10551
10552             function createShortMethods(names) {
10553               forEach(arguments, function(name) {
10554                 $http[name] = function(url, config) {
10555                   return $http(extend({}, config || {}, {
10556                     method: name,
10557                     url: url
10558                   }));
10559                 };
10560               });
10561             }
10562
10563
10564             function createShortMethodsWithData(name) {
10565               forEach(arguments, function(name) {
10566                 $http[name] = function(url, data, config) {
10567                   return $http(extend({}, config || {}, {
10568                     method: name,
10569                     url: url,
10570                     data: data
10571                   }));
10572                 };
10573               });
10574             }
10575
10576
10577             /**
10578              * Makes the request.
10579              *
10580              * !!! ACCESSES CLOSURE VARS:
10581              * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10582              */
10583             function sendReq(config, reqData) {
10584               var deferred = $q.defer(),
10585                   promise = deferred.promise,
10586                   cache,
10587                   cachedResp,
10588                   reqHeaders = config.headers,
10589                   url = buildUrl(config.url, config.paramSerializer(config.params));
10590
10591               $http.pendingRequests.push(config);
10592               promise.then(removePendingReq, removePendingReq);
10593
10594
10595               if ((config.cache || defaults.cache) && config.cache !== false &&
10596                   (config.method === 'GET' || config.method === 'JSONP')) {
10597                 cache = isObject(config.cache) ? config.cache
10598                       : isObject(defaults.cache) ? defaults.cache
10599                       : defaultCache;
10600               }
10601
10602               if (cache) {
10603                 cachedResp = cache.get(url);
10604                 if (isDefined(cachedResp)) {
10605                   if (isPromiseLike(cachedResp)) {
10606                     // cached request has already been sent, but there is no response yet
10607                     cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10608                   } else {
10609                     // serving from cache
10610                     if (isArray(cachedResp)) {
10611                       resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10612                     } else {
10613                       resolvePromise(cachedResp, 200, {}, 'OK');
10614                     }
10615                   }
10616                 } else {
10617                   // put the promise for the non-transformed response into cache as a placeholder
10618                   cache.put(url, promise);
10619                 }
10620               }
10621
10622
10623               // if we won't have the response in cache, set the xsrf headers and
10624               // send the request to the backend
10625               if (isUndefined(cachedResp)) {
10626                 var xsrfValue = urlIsSameOrigin(config.url)
10627                     ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10628                     : undefined;
10629                 if (xsrfValue) {
10630                   reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10631                 }
10632
10633                 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10634                     config.withCredentials, config.responseType);
10635               }
10636
10637               return promise;
10638
10639
10640               /**
10641                * Callback registered to $httpBackend():
10642                *  - caches the response if desired
10643                *  - resolves the raw $http promise
10644                *  - calls $apply
10645                */
10646               function done(status, response, headersString, statusText) {
10647                 if (cache) {
10648                   if (isSuccess(status)) {
10649                     cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10650                   } else {
10651                     // remove promise from the cache
10652                     cache.remove(url);
10653                   }
10654                 }
10655
10656                 function resolveHttpPromise() {
10657                   resolvePromise(response, status, headersString, statusText);
10658                 }
10659
10660                 if (useApplyAsync) {
10661                   $rootScope.$applyAsync(resolveHttpPromise);
10662                 } else {
10663                   resolveHttpPromise();
10664                   if (!$rootScope.$$phase) $rootScope.$apply();
10665                 }
10666               }
10667
10668
10669               /**
10670                * Resolves the raw $http promise.
10671                */
10672               function resolvePromise(response, status, headers, statusText) {
10673                 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10674                 status = status >= -1 ? status : 0;
10675
10676                 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10677                   data: response,
10678                   status: status,
10679                   headers: headersGetter(headers),
10680                   config: config,
10681                   statusText: statusText
10682                 });
10683               }
10684
10685               function resolvePromiseWithResult(result) {
10686                 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10687               }
10688
10689               function removePendingReq() {
10690                 var idx = $http.pendingRequests.indexOf(config);
10691                 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10692               }
10693             }
10694
10695
10696             function buildUrl(url, serializedParams) {
10697               if (serializedParams.length > 0) {
10698                 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10699               }
10700               return url;
10701             }
10702           }];
10703         }
10704
10705         /**
10706          * @ngdoc service
10707          * @name $xhrFactory
10708          *
10709          * @description
10710          * Factory function used to create XMLHttpRequest objects.
10711          *
10712          * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10713          *
10714          * ```
10715          * angular.module('myApp', [])
10716          * .factory('$xhrFactory', function() {
10717          *   return function createXhr(method, url) {
10718          *     return new window.XMLHttpRequest({mozSystem: true});
10719          *   };
10720          * });
10721          * ```
10722          *
10723          * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10724          * @param {string} url URL of the request.
10725          */
10726         function $xhrFactoryProvider() {
10727           this.$get = function() {
10728             return function createXhr() {
10729               return new window.XMLHttpRequest();
10730             };
10731           };
10732         }
10733
10734         /**
10735          * @ngdoc service
10736          * @name $httpBackend
10737          * @requires $window
10738          * @requires $document
10739          * @requires $xhrFactory
10740          *
10741          * @description
10742          * HTTP backend used by the {@link ng.$http service} that delegates to
10743          * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10744          *
10745          * You should never need to use this service directly, instead use the higher-level abstractions:
10746          * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10747          *
10748          * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10749          * $httpBackend} which can be trained with responses.
10750          */
10751         function $HttpBackendProvider() {
10752           this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10753             return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10754           }];
10755         }
10756
10757         function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10758           // TODO(vojta): fix the signature
10759           return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10760             $browser.$$incOutstandingRequestCount();
10761             url = url || $browser.url();
10762
10763             if (lowercase(method) == 'jsonp') {
10764               var callbackId = '_' + (callbacks.counter++).toString(36);
10765               callbacks[callbackId] = function(data) {
10766                 callbacks[callbackId].data = data;
10767                 callbacks[callbackId].called = true;
10768               };
10769
10770               var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10771                   callbackId, function(status, text) {
10772                 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10773                 callbacks[callbackId] = noop;
10774               });
10775             } else {
10776
10777               var xhr = createXhr(method, url);
10778
10779               xhr.open(method, url, true);
10780               forEach(headers, function(value, key) {
10781                 if (isDefined(value)) {
10782                     xhr.setRequestHeader(key, value);
10783                 }
10784               });
10785
10786               xhr.onload = function requestLoaded() {
10787                 var statusText = xhr.statusText || '';
10788
10789                 // responseText is the old-school way of retrieving response (supported by IE9)
10790                 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10791                 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10792
10793                 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10794                 var status = xhr.status === 1223 ? 204 : xhr.status;
10795
10796                 // fix status code when it is 0 (0 status is undocumented).
10797                 // Occurs when accessing file resources or on Android 4.1 stock browser
10798                 // while retrieving files from application cache.
10799                 if (status === 0) {
10800                   status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10801                 }
10802
10803                 completeRequest(callback,
10804                     status,
10805                     response,
10806                     xhr.getAllResponseHeaders(),
10807                     statusText);
10808               };
10809
10810               var requestError = function() {
10811                 // The response is always empty
10812                 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10813                 completeRequest(callback, -1, null, null, '');
10814               };
10815
10816               xhr.onerror = requestError;
10817               xhr.onabort = requestError;
10818
10819               if (withCredentials) {
10820                 xhr.withCredentials = true;
10821               }
10822
10823               if (responseType) {
10824                 try {
10825                   xhr.responseType = responseType;
10826                 } catch (e) {
10827                   // WebKit added support for the json responseType value on 09/03/2013
10828                   // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10829                   // known to throw when setting the value "json" as the response type. Other older
10830                   // browsers implementing the responseType
10831                   //
10832                   // The json response type can be ignored if not supported, because JSON payloads are
10833                   // parsed on the client-side regardless.
10834                   if (responseType !== 'json') {
10835                     throw e;
10836                   }
10837                 }
10838               }
10839
10840               xhr.send(isUndefined(post) ? null : post);
10841             }
10842
10843             if (timeout > 0) {
10844               var timeoutId = $browserDefer(timeoutRequest, timeout);
10845             } else if (isPromiseLike(timeout)) {
10846               timeout.then(timeoutRequest);
10847             }
10848
10849
10850             function timeoutRequest() {
10851               jsonpDone && jsonpDone();
10852               xhr && xhr.abort();
10853             }
10854
10855             function completeRequest(callback, status, response, headersString, statusText) {
10856               // cancel timeout and subsequent timeout promise resolution
10857               if (isDefined(timeoutId)) {
10858                 $browserDefer.cancel(timeoutId);
10859               }
10860               jsonpDone = xhr = null;
10861
10862               callback(status, response, headersString, statusText);
10863               $browser.$$completeOutstandingRequest(noop);
10864             }
10865           };
10866
10867           function jsonpReq(url, callbackId, done) {
10868             // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10869             // - fetches local scripts via XHR and evals them
10870             // - adds and immediately removes script elements from the document
10871             var script = rawDocument.createElement('script'), callback = null;
10872             script.type = "text/javascript";
10873             script.src = url;
10874             script.async = true;
10875
10876             callback = function(event) {
10877               removeEventListenerFn(script, "load", callback);
10878               removeEventListenerFn(script, "error", callback);
10879               rawDocument.body.removeChild(script);
10880               script = null;
10881               var status = -1;
10882               var text = "unknown";
10883
10884               if (event) {
10885                 if (event.type === "load" && !callbacks[callbackId].called) {
10886                   event = { type: "error" };
10887                 }
10888                 text = event.type;
10889                 status = event.type === "error" ? 404 : 200;
10890               }
10891
10892               if (done) {
10893                 done(status, text);
10894               }
10895             };
10896
10897             addEventListenerFn(script, "load", callback);
10898             addEventListenerFn(script, "error", callback);
10899             rawDocument.body.appendChild(script);
10900             return callback;
10901           }
10902         }
10903
10904         var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10905         $interpolateMinErr.throwNoconcat = function(text) {
10906           throw $interpolateMinErr('noconcat',
10907               "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10908               "interpolations that concatenate multiple expressions when a trusted value is " +
10909               "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10910         };
10911
10912         $interpolateMinErr.interr = function(text, err) {
10913           return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10914         };
10915
10916         /**
10917          * @ngdoc provider
10918          * @name $interpolateProvider
10919          *
10920          * @description
10921          *
10922          * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10923          *
10924          * @example
10925         <example module="customInterpolationApp">
10926         <file name="index.html">
10927         <script>
10928           var customInterpolationApp = angular.module('customInterpolationApp', []);
10929
10930           customInterpolationApp.config(function($interpolateProvider) {
10931             $interpolateProvider.startSymbol('//');
10932             $interpolateProvider.endSymbol('//');
10933           });
10934
10935
10936           customInterpolationApp.controller('DemoController', function() {
10937               this.label = "This binding is brought you by // interpolation symbols.";
10938           });
10939         </script>
10940         <div ng-app="App" ng-controller="DemoController as demo">
10941             //demo.label//
10942         </div>
10943         </file>
10944         <file name="protractor.js" type="protractor">
10945           it('should interpolate binding with custom symbols', function() {
10946             expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10947           });
10948         </file>
10949         </example>
10950          */
10951         function $InterpolateProvider() {
10952           var startSymbol = '{{';
10953           var endSymbol = '}}';
10954
10955           /**
10956            * @ngdoc method
10957            * @name $interpolateProvider#startSymbol
10958            * @description
10959            * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10960            *
10961            * @param {string=} value new value to set the starting symbol to.
10962            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10963            */
10964           this.startSymbol = function(value) {
10965             if (value) {
10966               startSymbol = value;
10967               return this;
10968             } else {
10969               return startSymbol;
10970             }
10971           };
10972
10973           /**
10974            * @ngdoc method
10975            * @name $interpolateProvider#endSymbol
10976            * @description
10977            * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10978            *
10979            * @param {string=} value new value to set the ending symbol to.
10980            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10981            */
10982           this.endSymbol = function(value) {
10983             if (value) {
10984               endSymbol = value;
10985               return this;
10986             } else {
10987               return endSymbol;
10988             }
10989           };
10990
10991
10992           this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10993             var startSymbolLength = startSymbol.length,
10994                 endSymbolLength = endSymbol.length,
10995                 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10996                 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10997
10998             function escape(ch) {
10999               return '\\\\\\' + ch;
11000             }
11001
11002             function unescapeText(text) {
11003               return text.replace(escapedStartRegexp, startSymbol).
11004                 replace(escapedEndRegexp, endSymbol);
11005             }
11006
11007             function stringify(value) {
11008               if (value == null) { // null || undefined
11009                 return '';
11010               }
11011               switch (typeof value) {
11012                 case 'string':
11013                   break;
11014                 case 'number':
11015                   value = '' + value;
11016                   break;
11017                 default:
11018                   value = toJson(value);
11019               }
11020
11021               return value;
11022             }
11023
11024             /**
11025              * @ngdoc service
11026              * @name $interpolate
11027              * @kind function
11028              *
11029              * @requires $parse
11030              * @requires $sce
11031              *
11032              * @description
11033              *
11034              * Compiles a string with markup into an interpolation function. This service is used by the
11035              * HTML {@link ng.$compile $compile} service for data binding. See
11036              * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11037              * interpolation markup.
11038              *
11039              *
11040              * ```js
11041              *   var $interpolate = ...; // injected
11042              *   var exp = $interpolate('Hello {{name | uppercase}}!');
11043              *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11044              * ```
11045              *
11046              * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11047              * `true`, the interpolation function will return `undefined` unless all embedded expressions
11048              * evaluate to a value other than `undefined`.
11049              *
11050              * ```js
11051              *   var $interpolate = ...; // injected
11052              *   var context = {greeting: 'Hello', name: undefined };
11053              *
11054              *   // default "forgiving" mode
11055              *   var exp = $interpolate('{{greeting}} {{name}}!');
11056              *   expect(exp(context)).toEqual('Hello !');
11057              *
11058              *   // "allOrNothing" mode
11059              *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11060              *   expect(exp(context)).toBeUndefined();
11061              *   context.name = 'Angular';
11062              *   expect(exp(context)).toEqual('Hello Angular!');
11063              * ```
11064              *
11065              * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11066              *
11067              * ####Escaped Interpolation
11068              * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11069              * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11070              * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11071              * or binding.
11072              *
11073              * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11074              * degree, while also enabling code examples to work without relying on the
11075              * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11076              *
11077              * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11078              * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11079              * interpolation start/end markers with their escaped counterparts.**
11080              *
11081              * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11082              * output when the $interpolate service processes the text. So, for HTML elements interpolated
11083              * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11084              * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11085              * this is typically useful only when user-data is used in rendering a template from the server, or
11086              * when otherwise untrusted data is used by a directive.
11087              *
11088              * <example>
11089              *  <file name="index.html">
11090              *    <div ng-init="username='A user'">
11091              *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11092              *        </p>
11093              *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11094              *        application, but fails to accomplish their task, because the server has correctly
11095              *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11096              *        characters.</p>
11097              *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11098              *        from the database by an administrator.</p>
11099              *    </div>
11100              *  </file>
11101              * </example>
11102              *
11103              * @param {string} text The text with markup to interpolate.
11104              * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11105              *    embedded expression in order to return an interpolation function. Strings with no
11106              *    embedded expression will return null for the interpolation function.
11107              * @param {string=} trustedContext when provided, the returned function passes the interpolated
11108              *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11109              *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11110              *    provides Strict Contextual Escaping for details.
11111              * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11112              *    unless all embedded expressions evaluate to a value other than `undefined`.
11113              * @returns {function(context)} an interpolation function which is used to compute the
11114              *    interpolated string. The function has these parameters:
11115              *
11116              * - `context`: evaluation context for all expressions embedded in the interpolated text
11117              */
11118             function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11119               allOrNothing = !!allOrNothing;
11120               var startIndex,
11121                   endIndex,
11122                   index = 0,
11123                   expressions = [],
11124                   parseFns = [],
11125                   textLength = text.length,
11126                   exp,
11127                   concat = [],
11128                   expressionPositions = [];
11129
11130               while (index < textLength) {
11131                 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11132                      ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11133                   if (index !== startIndex) {
11134                     concat.push(unescapeText(text.substring(index, startIndex)));
11135                   }
11136                   exp = text.substring(startIndex + startSymbolLength, endIndex);
11137                   expressions.push(exp);
11138                   parseFns.push($parse(exp, parseStringifyInterceptor));
11139                   index = endIndex + endSymbolLength;
11140                   expressionPositions.push(concat.length);
11141                   concat.push('');
11142                 } else {
11143                   // we did not find an interpolation, so we have to add the remainder to the separators array
11144                   if (index !== textLength) {
11145                     concat.push(unescapeText(text.substring(index)));
11146                   }
11147                   break;
11148                 }
11149               }
11150
11151               // Concatenating expressions makes it hard to reason about whether some combination of
11152               // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11153               // single expression be used for iframe[src], object[src], etc., we ensure that the value
11154               // that's used is assigned or constructed by some JS code somewhere that is more testable or
11155               // make it obvious that you bound the value to some user controlled value.  This helps reduce
11156               // the load when auditing for XSS issues.
11157               if (trustedContext && concat.length > 1) {
11158                   $interpolateMinErr.throwNoconcat(text);
11159               }
11160
11161               if (!mustHaveExpression || expressions.length) {
11162                 var compute = function(values) {
11163                   for (var i = 0, ii = expressions.length; i < ii; i++) {
11164                     if (allOrNothing && isUndefined(values[i])) return;
11165                     concat[expressionPositions[i]] = values[i];
11166                   }
11167                   return concat.join('');
11168                 };
11169
11170                 var getValue = function(value) {
11171                   return trustedContext ?
11172                     $sce.getTrusted(trustedContext, value) :
11173                     $sce.valueOf(value);
11174                 };
11175
11176                 return extend(function interpolationFn(context) {
11177                     var i = 0;
11178                     var ii = expressions.length;
11179                     var values = new Array(ii);
11180
11181                     try {
11182                       for (; i < ii; i++) {
11183                         values[i] = parseFns[i](context);
11184                       }
11185
11186                       return compute(values);
11187                     } catch (err) {
11188                       $exceptionHandler($interpolateMinErr.interr(text, err));
11189                     }
11190
11191                   }, {
11192                   // all of these properties are undocumented for now
11193                   exp: text, //just for compatibility with regular watchers created via $watch
11194                   expressions: expressions,
11195                   $$watchDelegate: function(scope, listener) {
11196                     var lastValue;
11197                     return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11198                       var currValue = compute(values);
11199                       if (isFunction(listener)) {
11200                         listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11201                       }
11202                       lastValue = currValue;
11203                     });
11204                   }
11205                 });
11206               }
11207
11208               function parseStringifyInterceptor(value) {
11209                 try {
11210                   value = getValue(value);
11211                   return allOrNothing && !isDefined(value) ? value : stringify(value);
11212                 } catch (err) {
11213                   $exceptionHandler($interpolateMinErr.interr(text, err));
11214                 }
11215               }
11216             }
11217
11218
11219             /**
11220              * @ngdoc method
11221              * @name $interpolate#startSymbol
11222              * @description
11223              * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11224              *
11225              * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11226              * the symbol.
11227              *
11228              * @returns {string} start symbol.
11229              */
11230             $interpolate.startSymbol = function() {
11231               return startSymbol;
11232             };
11233
11234
11235             /**
11236              * @ngdoc method
11237              * @name $interpolate#endSymbol
11238              * @description
11239              * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11240              *
11241              * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11242              * the symbol.
11243              *
11244              * @returns {string} end symbol.
11245              */
11246             $interpolate.endSymbol = function() {
11247               return endSymbol;
11248             };
11249
11250             return $interpolate;
11251           }];
11252         }
11253
11254         function $IntervalProvider() {
11255           this.$get = ['$rootScope', '$window', '$q', '$$q',
11256                function($rootScope,   $window,   $q,   $$q) {
11257             var intervals = {};
11258
11259
11260              /**
11261               * @ngdoc service
11262               * @name $interval
11263               *
11264               * @description
11265               * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11266               * milliseconds.
11267               *
11268               * The return value of registering an interval function is a promise. This promise will be
11269               * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11270               * run indefinitely if `count` is not defined. The value of the notification will be the
11271               * number of iterations that have run.
11272               * To cancel an interval, call `$interval.cancel(promise)`.
11273               *
11274               * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11275               * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11276               * time.
11277               *
11278               * <div class="alert alert-warning">
11279               * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11280               * with them.  In particular they are not automatically destroyed when a controller's scope or a
11281               * directive's element are destroyed.
11282               * You should take this into consideration and make sure to always cancel the interval at the
11283               * appropriate moment.  See the example below for more details on how and when to do this.
11284               * </div>
11285               *
11286               * @param {function()} fn A function that should be called repeatedly.
11287               * @param {number} delay Number of milliseconds between each function call.
11288               * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11289               *   indefinitely.
11290               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11291               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11292               * @param {...*=} Pass additional parameters to the executed function.
11293               * @returns {promise} A promise which will be notified on each iteration.
11294               *
11295               * @example
11296               * <example module="intervalExample">
11297               * <file name="index.html">
11298               *   <script>
11299               *     angular.module('intervalExample', [])
11300               *       .controller('ExampleController', ['$scope', '$interval',
11301               *         function($scope, $interval) {
11302               *           $scope.format = 'M/d/yy h:mm:ss a';
11303               *           $scope.blood_1 = 100;
11304               *           $scope.blood_2 = 120;
11305               *
11306               *           var stop;
11307               *           $scope.fight = function() {
11308               *             // Don't start a new fight if we are already fighting
11309               *             if ( angular.isDefined(stop) ) return;
11310               *
11311               *             stop = $interval(function() {
11312               *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11313               *                 $scope.blood_1 = $scope.blood_1 - 3;
11314               *                 $scope.blood_2 = $scope.blood_2 - 4;
11315               *               } else {
11316               *                 $scope.stopFight();
11317               *               }
11318               *             }, 100);
11319               *           };
11320               *
11321               *           $scope.stopFight = function() {
11322               *             if (angular.isDefined(stop)) {
11323               *               $interval.cancel(stop);
11324               *               stop = undefined;
11325               *             }
11326               *           };
11327               *
11328               *           $scope.resetFight = function() {
11329               *             $scope.blood_1 = 100;
11330               *             $scope.blood_2 = 120;
11331               *           };
11332               *
11333               *           $scope.$on('$destroy', function() {
11334               *             // Make sure that the interval is destroyed too
11335               *             $scope.stopFight();
11336               *           });
11337               *         }])
11338               *       // Register the 'myCurrentTime' directive factory method.
11339               *       // We inject $interval and dateFilter service since the factory method is DI.
11340               *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11341               *         function($interval, dateFilter) {
11342               *           // return the directive link function. (compile function not needed)
11343               *           return function(scope, element, attrs) {
11344               *             var format,  // date format
11345               *                 stopTime; // so that we can cancel the time updates
11346               *
11347               *             // used to update the UI
11348               *             function updateTime() {
11349               *               element.text(dateFilter(new Date(), format));
11350               *             }
11351               *
11352               *             // watch the expression, and update the UI on change.
11353               *             scope.$watch(attrs.myCurrentTime, function(value) {
11354               *               format = value;
11355               *               updateTime();
11356               *             });
11357               *
11358               *             stopTime = $interval(updateTime, 1000);
11359               *
11360               *             // listen on DOM destroy (removal) event, and cancel the next UI update
11361               *             // to prevent updating time after the DOM element was removed.
11362               *             element.on('$destroy', function() {
11363               *               $interval.cancel(stopTime);
11364               *             });
11365               *           }
11366               *         }]);
11367               *   </script>
11368               *
11369               *   <div>
11370               *     <div ng-controller="ExampleController">
11371               *       <label>Date format: <input ng-model="format"></label> <hr/>
11372               *       Current time is: <span my-current-time="format"></span>
11373               *       <hr/>
11374               *       Blood 1 : <font color='red'>{{blood_1}}</font>
11375               *       Blood 2 : <font color='red'>{{blood_2}}</font>
11376               *       <button type="button" data-ng-click="fight()">Fight</button>
11377               *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11378               *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11379               *     </div>
11380               *   </div>
11381               *
11382               * </file>
11383               * </example>
11384               */
11385             function interval(fn, delay, count, invokeApply) {
11386               var hasParams = arguments.length > 4,
11387                   args = hasParams ? sliceArgs(arguments, 4) : [],
11388                   setInterval = $window.setInterval,
11389                   clearInterval = $window.clearInterval,
11390                   iteration = 0,
11391                   skipApply = (isDefined(invokeApply) && !invokeApply),
11392                   deferred = (skipApply ? $$q : $q).defer(),
11393                   promise = deferred.promise;
11394
11395               count = isDefined(count) ? count : 0;
11396
11397               promise.then(null, null, (!hasParams) ? fn : function() {
11398                 fn.apply(null, args);
11399               });
11400
11401               promise.$$intervalId = setInterval(function tick() {
11402                 deferred.notify(iteration++);
11403
11404                 if (count > 0 && iteration >= count) {
11405                   deferred.resolve(iteration);
11406                   clearInterval(promise.$$intervalId);
11407                   delete intervals[promise.$$intervalId];
11408                 }
11409
11410                 if (!skipApply) $rootScope.$apply();
11411
11412               }, delay);
11413
11414               intervals[promise.$$intervalId] = deferred;
11415
11416               return promise;
11417             }
11418
11419
11420              /**
11421               * @ngdoc method
11422               * @name $interval#cancel
11423               *
11424               * @description
11425               * Cancels a task associated with the `promise`.
11426               *
11427               * @param {Promise=} promise returned by the `$interval` function.
11428               * @returns {boolean} Returns `true` if the task was successfully canceled.
11429               */
11430             interval.cancel = function(promise) {
11431               if (promise && promise.$$intervalId in intervals) {
11432                 intervals[promise.$$intervalId].reject('canceled');
11433                 $window.clearInterval(promise.$$intervalId);
11434                 delete intervals[promise.$$intervalId];
11435                 return true;
11436               }
11437               return false;
11438             };
11439
11440             return interval;
11441           }];
11442         }
11443
11444         /**
11445          * @ngdoc service
11446          * @name $locale
11447          *
11448          * @description
11449          * $locale service provides localization rules for various Angular components. As of right now the
11450          * only public api is:
11451          *
11452          * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11453          */
11454
11455         var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11456             DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11457         var $locationMinErr = minErr('$location');
11458
11459
11460         /**
11461          * Encode path using encodeUriSegment, ignoring forward slashes
11462          *
11463          * @param {string} path Path to encode
11464          * @returns {string}
11465          */
11466         function encodePath(path) {
11467           var segments = path.split('/'),
11468               i = segments.length;
11469
11470           while (i--) {
11471             segments[i] = encodeUriSegment(segments[i]);
11472           }
11473
11474           return segments.join('/');
11475         }
11476
11477         function parseAbsoluteUrl(absoluteUrl, locationObj) {
11478           var parsedUrl = urlResolve(absoluteUrl);
11479
11480           locationObj.$$protocol = parsedUrl.protocol;
11481           locationObj.$$host = parsedUrl.hostname;
11482           locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11483         }
11484
11485
11486         function parseAppUrl(relativeUrl, locationObj) {
11487           var prefixed = (relativeUrl.charAt(0) !== '/');
11488           if (prefixed) {
11489             relativeUrl = '/' + relativeUrl;
11490           }
11491           var match = urlResolve(relativeUrl);
11492           locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11493               match.pathname.substring(1) : match.pathname);
11494           locationObj.$$search = parseKeyValue(match.search);
11495           locationObj.$$hash = decodeURIComponent(match.hash);
11496
11497           // make sure path starts with '/';
11498           if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11499             locationObj.$$path = '/' + locationObj.$$path;
11500           }
11501         }
11502
11503
11504         /**
11505          *
11506          * @param {string} begin
11507          * @param {string} whole
11508          * @returns {string} returns text from whole after begin or undefined if it does not begin with
11509          *                   expected string.
11510          */
11511         function beginsWith(begin, whole) {
11512           if (whole.indexOf(begin) === 0) {
11513             return whole.substr(begin.length);
11514           }
11515         }
11516
11517
11518         function stripHash(url) {
11519           var index = url.indexOf('#');
11520           return index == -1 ? url : url.substr(0, index);
11521         }
11522
11523         function trimEmptyHash(url) {
11524           return url.replace(/(#.+)|#$/, '$1');
11525         }
11526
11527
11528         function stripFile(url) {
11529           return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11530         }
11531
11532         /* return the server only (scheme://host:port) */
11533         function serverBase(url) {
11534           return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11535         }
11536
11537
11538         /**
11539          * LocationHtml5Url represents an url
11540          * This object is exposed as $location service when HTML5 mode is enabled and supported
11541          *
11542          * @constructor
11543          * @param {string} appBase application base URL
11544          * @param {string} appBaseNoFile application base URL stripped of any filename
11545          * @param {string} basePrefix url path prefix
11546          */
11547         function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11548           this.$$html5 = true;
11549           basePrefix = basePrefix || '';
11550           parseAbsoluteUrl(appBase, this);
11551
11552
11553           /**
11554            * Parse given html5 (regular) url string into properties
11555            * @param {string} url HTML5 url
11556            * @private
11557            */
11558           this.$$parse = function(url) {
11559             var pathUrl = beginsWith(appBaseNoFile, url);
11560             if (!isString(pathUrl)) {
11561               throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11562                   appBaseNoFile);
11563             }
11564
11565             parseAppUrl(pathUrl, this);
11566
11567             if (!this.$$path) {
11568               this.$$path = '/';
11569             }
11570
11571             this.$$compose();
11572           };
11573
11574           /**
11575            * Compose url and update `absUrl` property
11576            * @private
11577            */
11578           this.$$compose = function() {
11579             var search = toKeyValue(this.$$search),
11580                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11581
11582             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11583             this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11584           };
11585
11586           this.$$parseLinkUrl = function(url, relHref) {
11587             if (relHref && relHref[0] === '#') {
11588               // special case for links to hash fragments:
11589               // keep the old url and only replace the hash fragment
11590               this.hash(relHref.slice(1));
11591               return true;
11592             }
11593             var appUrl, prevAppUrl;
11594             var rewrittenUrl;
11595
11596             if (isDefined(appUrl = beginsWith(appBase, url))) {
11597               prevAppUrl = appUrl;
11598               if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11599                 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11600               } else {
11601                 rewrittenUrl = appBase + prevAppUrl;
11602               }
11603             } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11604               rewrittenUrl = appBaseNoFile + appUrl;
11605             } else if (appBaseNoFile == url + '/') {
11606               rewrittenUrl = appBaseNoFile;
11607             }
11608             if (rewrittenUrl) {
11609               this.$$parse(rewrittenUrl);
11610             }
11611             return !!rewrittenUrl;
11612           };
11613         }
11614
11615
11616         /**
11617          * LocationHashbangUrl represents url
11618          * This object is exposed as $location service when developer doesn't opt into html5 mode.
11619          * It also serves as the base class for html5 mode fallback on legacy browsers.
11620          *
11621          * @constructor
11622          * @param {string} appBase application base URL
11623          * @param {string} appBaseNoFile application base URL stripped of any filename
11624          * @param {string} hashPrefix hashbang prefix
11625          */
11626         function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11627
11628           parseAbsoluteUrl(appBase, this);
11629
11630
11631           /**
11632            * Parse given hashbang url into properties
11633            * @param {string} url Hashbang url
11634            * @private
11635            */
11636           this.$$parse = function(url) {
11637             var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11638             var withoutHashUrl;
11639
11640             if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11641
11642               // The rest of the url starts with a hash so we have
11643               // got either a hashbang path or a plain hash fragment
11644               withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11645               if (isUndefined(withoutHashUrl)) {
11646                 // There was no hashbang prefix so we just have a hash fragment
11647                 withoutHashUrl = withoutBaseUrl;
11648               }
11649
11650             } else {
11651               // There was no hashbang path nor hash fragment:
11652               // If we are in HTML5 mode we use what is left as the path;
11653               // Otherwise we ignore what is left
11654               if (this.$$html5) {
11655                 withoutHashUrl = withoutBaseUrl;
11656               } else {
11657                 withoutHashUrl = '';
11658                 if (isUndefined(withoutBaseUrl)) {
11659                   appBase = url;
11660                   this.replace();
11661                 }
11662               }
11663             }
11664
11665             parseAppUrl(withoutHashUrl, this);
11666
11667             this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11668
11669             this.$$compose();
11670
11671             /*
11672              * In Windows, on an anchor node on documents loaded from
11673              * the filesystem, the browser will return a pathname
11674              * prefixed with the drive name ('/C:/path') when a
11675              * pathname without a drive is set:
11676              *  * a.setAttribute('href', '/foo')
11677              *   * a.pathname === '/C:/foo' //true
11678              *
11679              * Inside of Angular, we're always using pathnames that
11680              * do not include drive names for routing.
11681              */
11682             function removeWindowsDriveName(path, url, base) {
11683               /*
11684               Matches paths for file protocol on windows,
11685               such as /C:/foo/bar, and captures only /foo/bar.
11686               */
11687               var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11688
11689               var firstPathSegmentMatch;
11690
11691               //Get the relative path from the input URL.
11692               if (url.indexOf(base) === 0) {
11693                 url = url.replace(base, '');
11694               }
11695
11696               // The input URL intentionally contains a first path segment that ends with a colon.
11697               if (windowsFilePathExp.exec(url)) {
11698                 return path;
11699               }
11700
11701               firstPathSegmentMatch = windowsFilePathExp.exec(path);
11702               return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11703             }
11704           };
11705
11706           /**
11707            * Compose hashbang url and update `absUrl` property
11708            * @private
11709            */
11710           this.$$compose = function() {
11711             var search = toKeyValue(this.$$search),
11712                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11713
11714             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11715             this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11716           };
11717
11718           this.$$parseLinkUrl = function(url, relHref) {
11719             if (stripHash(appBase) == stripHash(url)) {
11720               this.$$parse(url);
11721               return true;
11722             }
11723             return false;
11724           };
11725         }
11726
11727
11728         /**
11729          * LocationHashbangUrl represents url
11730          * This object is exposed as $location service when html5 history api is enabled but the browser
11731          * does not support it.
11732          *
11733          * @constructor
11734          * @param {string} appBase application base URL
11735          * @param {string} appBaseNoFile application base URL stripped of any filename
11736          * @param {string} hashPrefix hashbang prefix
11737          */
11738         function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11739           this.$$html5 = true;
11740           LocationHashbangUrl.apply(this, arguments);
11741
11742           this.$$parseLinkUrl = function(url, relHref) {
11743             if (relHref && relHref[0] === '#') {
11744               // special case for links to hash fragments:
11745               // keep the old url and only replace the hash fragment
11746               this.hash(relHref.slice(1));
11747               return true;
11748             }
11749
11750             var rewrittenUrl;
11751             var appUrl;
11752
11753             if (appBase == stripHash(url)) {
11754               rewrittenUrl = url;
11755             } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11756               rewrittenUrl = appBase + hashPrefix + appUrl;
11757             } else if (appBaseNoFile === url + '/') {
11758               rewrittenUrl = appBaseNoFile;
11759             }
11760             if (rewrittenUrl) {
11761               this.$$parse(rewrittenUrl);
11762             }
11763             return !!rewrittenUrl;
11764           };
11765
11766           this.$$compose = function() {
11767             var search = toKeyValue(this.$$search),
11768                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11769
11770             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11771             // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11772             this.$$absUrl = appBase + hashPrefix + this.$$url;
11773           };
11774
11775         }
11776
11777
11778         var locationPrototype = {
11779
11780           /**
11781            * Are we in html5 mode?
11782            * @private
11783            */
11784           $$html5: false,
11785
11786           /**
11787            * Has any change been replacing?
11788            * @private
11789            */
11790           $$replace: false,
11791
11792           /**
11793            * @ngdoc method
11794            * @name $location#absUrl
11795            *
11796            * @description
11797            * This method is getter only.
11798            *
11799            * Return full url representation with all segments encoded according to rules specified in
11800            * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11801            *
11802            *
11803            * ```js
11804            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11805            * var absUrl = $location.absUrl();
11806            * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11807            * ```
11808            *
11809            * @return {string} full url
11810            */
11811           absUrl: locationGetter('$$absUrl'),
11812
11813           /**
11814            * @ngdoc method
11815            * @name $location#url
11816            *
11817            * @description
11818            * This method is getter / setter.
11819            *
11820            * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11821            *
11822            * Change path, search and hash, when called with parameter and return `$location`.
11823            *
11824            *
11825            * ```js
11826            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11827            * var url = $location.url();
11828            * // => "/some/path?foo=bar&baz=xoxo"
11829            * ```
11830            *
11831            * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11832            * @return {string} url
11833            */
11834           url: function(url) {
11835             if (isUndefined(url)) {
11836               return this.$$url;
11837             }
11838
11839             var match = PATH_MATCH.exec(url);
11840             if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11841             if (match[2] || match[1] || url === '') this.search(match[3] || '');
11842             this.hash(match[5] || '');
11843
11844             return this;
11845           },
11846
11847           /**
11848            * @ngdoc method
11849            * @name $location#protocol
11850            *
11851            * @description
11852            * This method is getter only.
11853            *
11854            * Return protocol of current url.
11855            *
11856            *
11857            * ```js
11858            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11859            * var protocol = $location.protocol();
11860            * // => "http"
11861            * ```
11862            *
11863            * @return {string} protocol of current url
11864            */
11865           protocol: locationGetter('$$protocol'),
11866
11867           /**
11868            * @ngdoc method
11869            * @name $location#host
11870            *
11871            * @description
11872            * This method is getter only.
11873            *
11874            * Return host of current url.
11875            *
11876            * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11877            *
11878            *
11879            * ```js
11880            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11881            * var host = $location.host();
11882            * // => "example.com"
11883            *
11884            * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11885            * host = $location.host();
11886            * // => "example.com"
11887            * host = location.host;
11888            * // => "example.com:8080"
11889            * ```
11890            *
11891            * @return {string} host of current url.
11892            */
11893           host: locationGetter('$$host'),
11894
11895           /**
11896            * @ngdoc method
11897            * @name $location#port
11898            *
11899            * @description
11900            * This method is getter only.
11901            *
11902            * Return port of current url.
11903            *
11904            *
11905            * ```js
11906            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11907            * var port = $location.port();
11908            * // => 80
11909            * ```
11910            *
11911            * @return {Number} port
11912            */
11913           port: locationGetter('$$port'),
11914
11915           /**
11916            * @ngdoc method
11917            * @name $location#path
11918            *
11919            * @description
11920            * This method is getter / setter.
11921            *
11922            * Return path of current url when called without any parameter.
11923            *
11924            * Change path when called with parameter and return `$location`.
11925            *
11926            * Note: Path should always begin with forward slash (/), this method will add the forward slash
11927            * if it is missing.
11928            *
11929            *
11930            * ```js
11931            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11932            * var path = $location.path();
11933            * // => "/some/path"
11934            * ```
11935            *
11936            * @param {(string|number)=} path New path
11937            * @return {string} path
11938            */
11939           path: locationGetterSetter('$$path', function(path) {
11940             path = path !== null ? path.toString() : '';
11941             return path.charAt(0) == '/' ? path : '/' + path;
11942           }),
11943
11944           /**
11945            * @ngdoc method
11946            * @name $location#search
11947            *
11948            * @description
11949            * This method is getter / setter.
11950            *
11951            * Return search part (as object) of current url when called without any parameter.
11952            *
11953            * Change search part when called with parameter and return `$location`.
11954            *
11955            *
11956            * ```js
11957            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11958            * var searchObject = $location.search();
11959            * // => {foo: 'bar', baz: 'xoxo'}
11960            *
11961            * // set foo to 'yipee'
11962            * $location.search('foo', 'yipee');
11963            * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11964            * ```
11965            *
11966            * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11967            * hash object.
11968            *
11969            * When called with a single argument the method acts as a setter, setting the `search` component
11970            * of `$location` to the specified value.
11971            *
11972            * If the argument is a hash object containing an array of values, these values will be encoded
11973            * as duplicate search parameters in the url.
11974            *
11975            * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11976            * will override only a single search property.
11977            *
11978            * If `paramValue` is an array, it will override the property of the `search` component of
11979            * `$location` specified via the first argument.
11980            *
11981            * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11982            *
11983            * If `paramValue` is `true`, the property specified via the first argument will be added with no
11984            * value nor trailing equal sign.
11985            *
11986            * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11987            * one or more arguments returns `$location` object itself.
11988            */
11989           search: function(search, paramValue) {
11990             switch (arguments.length) {
11991               case 0:
11992                 return this.$$search;
11993               case 1:
11994                 if (isString(search) || isNumber(search)) {
11995                   search = search.toString();
11996                   this.$$search = parseKeyValue(search);
11997                 } else if (isObject(search)) {
11998                   search = copy(search, {});
11999                   // remove object undefined or null properties
12000                   forEach(search, function(value, key) {
12001                     if (value == null) delete search[key];
12002                   });
12003
12004                   this.$$search = search;
12005                 } else {
12006                   throw $locationMinErr('isrcharg',
12007                       'The first argument of the `$location#search()` call must be a string or an object.');
12008                 }
12009                 break;
12010               default:
12011                 if (isUndefined(paramValue) || paramValue === null) {
12012                   delete this.$$search[search];
12013                 } else {
12014                   this.$$search[search] = paramValue;
12015                 }
12016             }
12017
12018             this.$$compose();
12019             return this;
12020           },
12021
12022           /**
12023            * @ngdoc method
12024            * @name $location#hash
12025            *
12026            * @description
12027            * This method is getter / setter.
12028            *
12029            * Returns the hash fragment when called without any parameters.
12030            *
12031            * Changes the hash fragment when called with a parameter and returns `$location`.
12032            *
12033            *
12034            * ```js
12035            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12036            * var hash = $location.hash();
12037            * // => "hashValue"
12038            * ```
12039            *
12040            * @param {(string|number)=} hash New hash fragment
12041            * @return {string} hash
12042            */
12043           hash: locationGetterSetter('$$hash', function(hash) {
12044             return hash !== null ? hash.toString() : '';
12045           }),
12046
12047           /**
12048            * @ngdoc method
12049            * @name $location#replace
12050            *
12051            * @description
12052            * If called, all changes to $location during the current `$digest` will replace the current history
12053            * record, instead of adding a new one.
12054            */
12055           replace: function() {
12056             this.$$replace = true;
12057             return this;
12058           }
12059         };
12060
12061         forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12062           Location.prototype = Object.create(locationPrototype);
12063
12064           /**
12065            * @ngdoc method
12066            * @name $location#state
12067            *
12068            * @description
12069            * This method is getter / setter.
12070            *
12071            * Return the history state object when called without any parameter.
12072            *
12073            * Change the history state object when called with one parameter and return `$location`.
12074            * The state object is later passed to `pushState` or `replaceState`.
12075            *
12076            * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12077            * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12078            * older browsers (like IE9 or Android < 4.0), don't use this method.
12079            *
12080            * @param {object=} state State object for pushState or replaceState
12081            * @return {object} state
12082            */
12083           Location.prototype.state = function(state) {
12084             if (!arguments.length) {
12085               return this.$$state;
12086             }
12087
12088             if (Location !== LocationHtml5Url || !this.$$html5) {
12089               throw $locationMinErr('nostate', 'History API state support is available only ' +
12090                 'in HTML5 mode and only in browsers supporting HTML5 History API');
12091             }
12092             // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12093             // but we're changing the $$state reference to $browser.state() during the $digest
12094             // so the modification window is narrow.
12095             this.$$state = isUndefined(state) ? null : state;
12096
12097             return this;
12098           };
12099         });
12100
12101
12102         function locationGetter(property) {
12103           return function() {
12104             return this[property];
12105           };
12106         }
12107
12108
12109         function locationGetterSetter(property, preprocess) {
12110           return function(value) {
12111             if (isUndefined(value)) {
12112               return this[property];
12113             }
12114
12115             this[property] = preprocess(value);
12116             this.$$compose();
12117
12118             return this;
12119           };
12120         }
12121
12122
12123         /**
12124          * @ngdoc service
12125          * @name $location
12126          *
12127          * @requires $rootElement
12128          *
12129          * @description
12130          * The $location service parses the URL in the browser address bar (based on the
12131          * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12132          * available to your application. Changes to the URL in the address bar are reflected into
12133          * $location service and changes to $location are reflected into the browser address bar.
12134          *
12135          * **The $location service:**
12136          *
12137          * - Exposes the current URL in the browser address bar, so you can
12138          *   - Watch and observe the URL.
12139          *   - Change the URL.
12140          * - Synchronizes the URL with the browser when the user
12141          *   - Changes the address bar.
12142          *   - Clicks the back or forward button (or clicks a History link).
12143          *   - Clicks on a link.
12144          * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12145          *
12146          * For more information see {@link guide/$location Developer Guide: Using $location}
12147          */
12148
12149         /**
12150          * @ngdoc provider
12151          * @name $locationProvider
12152          * @description
12153          * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12154          */
12155         function $LocationProvider() {
12156           var hashPrefix = '',
12157               html5Mode = {
12158                 enabled: false,
12159                 requireBase: true,
12160                 rewriteLinks: true
12161               };
12162
12163           /**
12164            * @ngdoc method
12165            * @name $locationProvider#hashPrefix
12166            * @description
12167            * @param {string=} prefix Prefix for hash part (containing path and search)
12168            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12169            */
12170           this.hashPrefix = function(prefix) {
12171             if (isDefined(prefix)) {
12172               hashPrefix = prefix;
12173               return this;
12174             } else {
12175               return hashPrefix;
12176             }
12177           };
12178
12179           /**
12180            * @ngdoc method
12181            * @name $locationProvider#html5Mode
12182            * @description
12183            * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12184            *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12185            *   properties:
12186            *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12187            *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12188            *     support `pushState`.
12189            *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12190            *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12191            *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12192            *     See the {@link guide/$location $location guide for more information}
12193            *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12194            *     enables/disables url rewriting for relative links.
12195            *
12196            * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12197            */
12198           this.html5Mode = function(mode) {
12199             if (isBoolean(mode)) {
12200               html5Mode.enabled = mode;
12201               return this;
12202             } else if (isObject(mode)) {
12203
12204               if (isBoolean(mode.enabled)) {
12205                 html5Mode.enabled = mode.enabled;
12206               }
12207
12208               if (isBoolean(mode.requireBase)) {
12209                 html5Mode.requireBase = mode.requireBase;
12210               }
12211
12212               if (isBoolean(mode.rewriteLinks)) {
12213                 html5Mode.rewriteLinks = mode.rewriteLinks;
12214               }
12215
12216               return this;
12217             } else {
12218               return html5Mode;
12219             }
12220           };
12221
12222           /**
12223            * @ngdoc event
12224            * @name $location#$locationChangeStart
12225            * @eventType broadcast on root scope
12226            * @description
12227            * Broadcasted before a URL will change.
12228            *
12229            * This change can be prevented by calling
12230            * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12231            * details about event object. Upon successful change
12232            * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12233            *
12234            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12235            * the browser supports the HTML5 History API.
12236            *
12237            * @param {Object} angularEvent Synthetic event object.
12238            * @param {string} newUrl New URL
12239            * @param {string=} oldUrl URL that was before it was changed.
12240            * @param {string=} newState New history state object
12241            * @param {string=} oldState History state object that was before it was changed.
12242            */
12243
12244           /**
12245            * @ngdoc event
12246            * @name $location#$locationChangeSuccess
12247            * @eventType broadcast on root scope
12248            * @description
12249            * Broadcasted after a URL was changed.
12250            *
12251            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12252            * the browser supports the HTML5 History API.
12253            *
12254            * @param {Object} angularEvent Synthetic event object.
12255            * @param {string} newUrl New URL
12256            * @param {string=} oldUrl URL that was before it was changed.
12257            * @param {string=} newState New history state object
12258            * @param {string=} oldState History state object that was before it was changed.
12259            */
12260
12261           this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12262               function($rootScope, $browser, $sniffer, $rootElement, $window) {
12263             var $location,
12264                 LocationMode,
12265                 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12266                 initialUrl = $browser.url(),
12267                 appBase;
12268
12269             if (html5Mode.enabled) {
12270               if (!baseHref && html5Mode.requireBase) {
12271                 throw $locationMinErr('nobase',
12272                   "$location in HTML5 mode requires a <base> tag to be present!");
12273               }
12274               appBase = serverBase(initialUrl) + (baseHref || '/');
12275               LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12276             } else {
12277               appBase = stripHash(initialUrl);
12278               LocationMode = LocationHashbangUrl;
12279             }
12280             var appBaseNoFile = stripFile(appBase);
12281
12282             $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12283             $location.$$parseLinkUrl(initialUrl, initialUrl);
12284
12285             $location.$$state = $browser.state();
12286
12287             var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12288
12289             function setBrowserUrlWithFallback(url, replace, state) {
12290               var oldUrl = $location.url();
12291               var oldState = $location.$$state;
12292               try {
12293                 $browser.url(url, replace, state);
12294
12295                 // Make sure $location.state() returns referentially identical (not just deeply equal)
12296                 // state object; this makes possible quick checking if the state changed in the digest
12297                 // loop. Checking deep equality would be too expensive.
12298                 $location.$$state = $browser.state();
12299               } catch (e) {
12300                 // Restore old values if pushState fails
12301                 $location.url(oldUrl);
12302                 $location.$$state = oldState;
12303
12304                 throw e;
12305               }
12306             }
12307
12308             $rootElement.on('click', function(event) {
12309               // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12310               // currently we open nice url link and redirect then
12311
12312               if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12313
12314               var elm = jqLite(event.target);
12315
12316               // traverse the DOM up to find first A tag
12317               while (nodeName_(elm[0]) !== 'a') {
12318                 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12319                 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12320               }
12321
12322               var absHref = elm.prop('href');
12323               // get the actual href attribute - see
12324               // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12325               var relHref = elm.attr('href') || elm.attr('xlink:href');
12326
12327               if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12328                 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12329                 // an animation.
12330                 absHref = urlResolve(absHref.animVal).href;
12331               }
12332
12333               // Ignore when url is started with javascript: or mailto:
12334               if (IGNORE_URI_REGEXP.test(absHref)) return;
12335
12336               if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12337                 if ($location.$$parseLinkUrl(absHref, relHref)) {
12338                   // We do a preventDefault for all urls that are part of the angular application,
12339                   // in html5mode and also without, so that we are able to abort navigation without
12340                   // getting double entries in the location history.
12341                   event.preventDefault();
12342                   // update location manually
12343                   if ($location.absUrl() != $browser.url()) {
12344                     $rootScope.$apply();
12345                     // hack to work around FF6 bug 684208 when scenario runner clicks on links
12346                     $window.angular['ff-684208-preventDefault'] = true;
12347                   }
12348                 }
12349               }
12350             });
12351
12352
12353             // rewrite hashbang url <> html5 url
12354             if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12355               $browser.url($location.absUrl(), true);
12356             }
12357
12358             var initializing = true;
12359
12360             // update $location when $browser url changes
12361             $browser.onUrlChange(function(newUrl, newState) {
12362
12363               if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12364                 // If we are navigating outside of the app then force a reload
12365                 $window.location.href = newUrl;
12366                 return;
12367               }
12368
12369               $rootScope.$evalAsync(function() {
12370                 var oldUrl = $location.absUrl();
12371                 var oldState = $location.$$state;
12372                 var defaultPrevented;
12373                 newUrl = trimEmptyHash(newUrl);
12374                 $location.$$parse(newUrl);
12375                 $location.$$state = newState;
12376
12377                 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12378                     newState, oldState).defaultPrevented;
12379
12380                 // if the location was changed by a `$locationChangeStart` handler then stop
12381                 // processing this location change
12382                 if ($location.absUrl() !== newUrl) return;
12383
12384                 if (defaultPrevented) {
12385                   $location.$$parse(oldUrl);
12386                   $location.$$state = oldState;
12387                   setBrowserUrlWithFallback(oldUrl, false, oldState);
12388                 } else {
12389                   initializing = false;
12390                   afterLocationChange(oldUrl, oldState);
12391                 }
12392               });
12393               if (!$rootScope.$$phase) $rootScope.$digest();
12394             });
12395
12396             // update browser
12397             $rootScope.$watch(function $locationWatch() {
12398               var oldUrl = trimEmptyHash($browser.url());
12399               var newUrl = trimEmptyHash($location.absUrl());
12400               var oldState = $browser.state();
12401               var currentReplace = $location.$$replace;
12402               var urlOrStateChanged = oldUrl !== newUrl ||
12403                 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12404
12405               if (initializing || urlOrStateChanged) {
12406                 initializing = false;
12407
12408                 $rootScope.$evalAsync(function() {
12409                   var newUrl = $location.absUrl();
12410                   var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12411                       $location.$$state, oldState).defaultPrevented;
12412
12413                   // if the location was changed by a `$locationChangeStart` handler then stop
12414                   // processing this location change
12415                   if ($location.absUrl() !== newUrl) return;
12416
12417                   if (defaultPrevented) {
12418                     $location.$$parse(oldUrl);
12419                     $location.$$state = oldState;
12420                   } else {
12421                     if (urlOrStateChanged) {
12422                       setBrowserUrlWithFallback(newUrl, currentReplace,
12423                                                 oldState === $location.$$state ? null : $location.$$state);
12424                     }
12425                     afterLocationChange(oldUrl, oldState);
12426                   }
12427                 });
12428               }
12429
12430               $location.$$replace = false;
12431
12432               // we don't need to return anything because $evalAsync will make the digest loop dirty when
12433               // there is a change
12434             });
12435
12436             return $location;
12437
12438             function afterLocationChange(oldUrl, oldState) {
12439               $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12440                 $location.$$state, oldState);
12441             }
12442         }];
12443         }
12444
12445         /**
12446          * @ngdoc service
12447          * @name $log
12448          * @requires $window
12449          *
12450          * @description
12451          * Simple service for logging. Default implementation safely writes the message
12452          * into the browser's console (if present).
12453          *
12454          * The main purpose of this service is to simplify debugging and troubleshooting.
12455          *
12456          * The default is to log `debug` messages. You can use
12457          * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12458          *
12459          * @example
12460            <example module="logExample">
12461              <file name="script.js">
12462                angular.module('logExample', [])
12463                  .controller('LogController', ['$scope', '$log', function($scope, $log) {
12464                    $scope.$log = $log;
12465                    $scope.message = 'Hello World!';
12466                  }]);
12467              </file>
12468              <file name="index.html">
12469                <div ng-controller="LogController">
12470                  <p>Reload this page with open console, enter text and hit the log button...</p>
12471                  <label>Message:
12472                  <input type="text" ng-model="message" /></label>
12473                  <button ng-click="$log.log(message)">log</button>
12474                  <button ng-click="$log.warn(message)">warn</button>
12475                  <button ng-click="$log.info(message)">info</button>
12476                  <button ng-click="$log.error(message)">error</button>
12477                  <button ng-click="$log.debug(message)">debug</button>
12478                </div>
12479              </file>
12480            </example>
12481          */
12482
12483         /**
12484          * @ngdoc provider
12485          * @name $logProvider
12486          * @description
12487          * Use the `$logProvider` to configure how the application logs messages
12488          */
12489         function $LogProvider() {
12490           var debug = true,
12491               self = this;
12492
12493           /**
12494            * @ngdoc method
12495            * @name $logProvider#debugEnabled
12496            * @description
12497            * @param {boolean=} flag enable or disable debug level messages
12498            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12499            */
12500           this.debugEnabled = function(flag) {
12501             if (isDefined(flag)) {
12502               debug = flag;
12503             return this;
12504             } else {
12505               return debug;
12506             }
12507           };
12508
12509           this.$get = ['$window', function($window) {
12510             return {
12511               /**
12512                * @ngdoc method
12513                * @name $log#log
12514                *
12515                * @description
12516                * Write a log message
12517                */
12518               log: consoleLog('log'),
12519
12520               /**
12521                * @ngdoc method
12522                * @name $log#info
12523                *
12524                * @description
12525                * Write an information message
12526                */
12527               info: consoleLog('info'),
12528
12529               /**
12530                * @ngdoc method
12531                * @name $log#warn
12532                *
12533                * @description
12534                * Write a warning message
12535                */
12536               warn: consoleLog('warn'),
12537
12538               /**
12539                * @ngdoc method
12540                * @name $log#error
12541                *
12542                * @description
12543                * Write an error message
12544                */
12545               error: consoleLog('error'),
12546
12547               /**
12548                * @ngdoc method
12549                * @name $log#debug
12550                *
12551                * @description
12552                * Write a debug message
12553                */
12554               debug: (function() {
12555                 var fn = consoleLog('debug');
12556
12557                 return function() {
12558                   if (debug) {
12559                     fn.apply(self, arguments);
12560                   }
12561                 };
12562               }())
12563             };
12564
12565             function formatError(arg) {
12566               if (arg instanceof Error) {
12567                 if (arg.stack) {
12568                   arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12569                       ? 'Error: ' + arg.message + '\n' + arg.stack
12570                       : arg.stack;
12571                 } else if (arg.sourceURL) {
12572                   arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12573                 }
12574               }
12575               return arg;
12576             }
12577
12578             function consoleLog(type) {
12579               var console = $window.console || {},
12580                   logFn = console[type] || console.log || noop,
12581                   hasApply = false;
12582
12583               // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12584               // The reason behind this is that console.log has type "object" in IE8...
12585               try {
12586                 hasApply = !!logFn.apply;
12587               } catch (e) {}
12588
12589               if (hasApply) {
12590                 return function() {
12591                   var args = [];
12592                   forEach(arguments, function(arg) {
12593                     args.push(formatError(arg));
12594                   });
12595                   return logFn.apply(console, args);
12596                 };
12597               }
12598
12599               // we are IE which either doesn't have window.console => this is noop and we do nothing,
12600               // or we are IE where console.log doesn't have apply so we log at least first 2 args
12601               return function(arg1, arg2) {
12602                 logFn(arg1, arg2 == null ? '' : arg2);
12603               };
12604             }
12605           }];
12606         }
12607
12608         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12609          *     Any commits to this file should be reviewed with security in mind.  *
12610          *   Changes to this file can potentially create security vulnerabilities. *
12611          *          An approval from 2 Core members with history of modifying      *
12612          *                         this file is required.                          *
12613          *                                                                         *
12614          *  Does the change somehow allow for arbitrary javascript to be executed? *
12615          *    Or allows for someone to change the prototype of built-in objects?   *
12616          *     Or gives undesired access to variables likes document or window?    *
12617          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12618
12619         var $parseMinErr = minErr('$parse');
12620
12621         // Sandboxing Angular Expressions
12622         // ------------------------------
12623         // Angular expressions are generally considered safe because these expressions only have direct
12624         // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12625         // obtaining a reference to native JS functions such as the Function constructor.
12626         //
12627         // As an example, consider the following Angular expression:
12628         //
12629         //   {}.toString.constructor('alert("evil JS code")')
12630         //
12631         // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12632         // against the expression language, but not to prevent exploits that were enabled by exposing
12633         // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12634         // practice and therefore we are not even trying to protect against interaction with an object
12635         // explicitly exposed in this way.
12636         //
12637         // In general, it is not possible to access a Window object from an angular expression unless a
12638         // window or some DOM object that has a reference to window is published onto a Scope.
12639         // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12640         // native objects.
12641         //
12642         // See https://docs.angularjs.org/guide/security
12643
12644
12645         function ensureSafeMemberName(name, fullExpression) {
12646           if (name === "__defineGetter__" || name === "__defineSetter__"
12647               || name === "__lookupGetter__" || name === "__lookupSetter__"
12648               || name === "__proto__") {
12649             throw $parseMinErr('isecfld',
12650                 'Attempting to access a disallowed field in Angular expressions! '
12651                 + 'Expression: {0}', fullExpression);
12652           }
12653           return name;
12654         }
12655
12656         function getStringValue(name, fullExpression) {
12657           // From the JavaScript docs:
12658           // Property names must be strings. This means that non-string objects cannot be used
12659           // as keys in an object. Any non-string object, including a number, is typecasted
12660           // into a string via the toString method.
12661           //
12662           // So, to ensure that we are checking the same `name` that JavaScript would use,
12663           // we cast it to a string, if possible.
12664           // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12665           // this is, this will handle objects that misbehave.
12666           name = name + '';
12667           if (!isString(name)) {
12668             throw $parseMinErr('iseccst',
12669                 'Cannot convert object to primitive value! '
12670                 + 'Expression: {0}', fullExpression);
12671           }
12672           return name;
12673         }
12674
12675         function ensureSafeObject(obj, fullExpression) {
12676           // nifty check if obj is Function that is fast and works across iframes and other contexts
12677           if (obj) {
12678             if (obj.constructor === obj) {
12679               throw $parseMinErr('isecfn',
12680                   'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12681                   fullExpression);
12682             } else if (// isWindow(obj)
12683                 obj.window === obj) {
12684               throw $parseMinErr('isecwindow',
12685                   'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12686                   fullExpression);
12687             } else if (// isElement(obj)
12688                 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12689               throw $parseMinErr('isecdom',
12690                   'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12691                   fullExpression);
12692             } else if (// block Object so that we can't get hold of dangerous Object.* methods
12693                 obj === Object) {
12694               throw $parseMinErr('isecobj',
12695                   'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12696                   fullExpression);
12697             }
12698           }
12699           return obj;
12700         }
12701
12702         var CALL = Function.prototype.call;
12703         var APPLY = Function.prototype.apply;
12704         var BIND = Function.prototype.bind;
12705
12706         function ensureSafeFunction(obj, fullExpression) {
12707           if (obj) {
12708             if (obj.constructor === obj) {
12709               throw $parseMinErr('isecfn',
12710                 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12711                 fullExpression);
12712             } else if (obj === CALL || obj === APPLY || obj === BIND) {
12713               throw $parseMinErr('isecff',
12714                 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12715                 fullExpression);
12716             }
12717           }
12718         }
12719
12720         function ensureSafeAssignContext(obj, fullExpression) {
12721           if (obj) {
12722             if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12723                 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12724               throw $parseMinErr('isecaf',
12725                 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12726             }
12727           }
12728         }
12729
12730         var OPERATORS = createMap();
12731         forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12732         var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12733
12734
12735         /////////////////////////////////////////
12736
12737
12738         /**
12739          * @constructor
12740          */
12741         var Lexer = function(options) {
12742           this.options = options;
12743         };
12744
12745         Lexer.prototype = {
12746           constructor: Lexer,
12747
12748           lex: function(text) {
12749             this.text = text;
12750             this.index = 0;
12751             this.tokens = [];
12752
12753             while (this.index < this.text.length) {
12754               var ch = this.text.charAt(this.index);
12755               if (ch === '"' || ch === "'") {
12756                 this.readString(ch);
12757               } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12758                 this.readNumber();
12759               } else if (this.isIdent(ch)) {
12760                 this.readIdent();
12761               } else if (this.is(ch, '(){}[].,;:?')) {
12762                 this.tokens.push({index: this.index, text: ch});
12763                 this.index++;
12764               } else if (this.isWhitespace(ch)) {
12765                 this.index++;
12766               } else {
12767                 var ch2 = ch + this.peek();
12768                 var ch3 = ch2 + this.peek(2);
12769                 var op1 = OPERATORS[ch];
12770                 var op2 = OPERATORS[ch2];
12771                 var op3 = OPERATORS[ch3];
12772                 if (op1 || op2 || op3) {
12773                   var token = op3 ? ch3 : (op2 ? ch2 : ch);
12774                   this.tokens.push({index: this.index, text: token, operator: true});
12775                   this.index += token.length;
12776                 } else {
12777                   this.throwError('Unexpected next character ', this.index, this.index + 1);
12778                 }
12779               }
12780             }
12781             return this.tokens;
12782           },
12783
12784           is: function(ch, chars) {
12785             return chars.indexOf(ch) !== -1;
12786           },
12787
12788           peek: function(i) {
12789             var num = i || 1;
12790             return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12791           },
12792
12793           isNumber: function(ch) {
12794             return ('0' <= ch && ch <= '9') && typeof ch === "string";
12795           },
12796
12797           isWhitespace: function(ch) {
12798             // IE treats non-breaking space as \u00A0
12799             return (ch === ' ' || ch === '\r' || ch === '\t' ||
12800                     ch === '\n' || ch === '\v' || ch === '\u00A0');
12801           },
12802
12803           isIdent: function(ch) {
12804             return ('a' <= ch && ch <= 'z' ||
12805                     'A' <= ch && ch <= 'Z' ||
12806                     '_' === ch || ch === '$');
12807           },
12808
12809           isExpOperator: function(ch) {
12810             return (ch === '-' || ch === '+' || this.isNumber(ch));
12811           },
12812
12813           throwError: function(error, start, end) {
12814             end = end || this.index;
12815             var colStr = (isDefined(start)
12816                     ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12817                     : ' ' + end);
12818             throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12819                 error, colStr, this.text);
12820           },
12821
12822           readNumber: function() {
12823             var number = '';
12824             var start = this.index;
12825             while (this.index < this.text.length) {
12826               var ch = lowercase(this.text.charAt(this.index));
12827               if (ch == '.' || this.isNumber(ch)) {
12828                 number += ch;
12829               } else {
12830                 var peekCh = this.peek();
12831                 if (ch == 'e' && this.isExpOperator(peekCh)) {
12832                   number += ch;
12833                 } else if (this.isExpOperator(ch) &&
12834                     peekCh && this.isNumber(peekCh) &&
12835                     number.charAt(number.length - 1) == 'e') {
12836                   number += ch;
12837                 } else if (this.isExpOperator(ch) &&
12838                     (!peekCh || !this.isNumber(peekCh)) &&
12839                     number.charAt(number.length - 1) == 'e') {
12840                   this.throwError('Invalid exponent');
12841                 } else {
12842                   break;
12843                 }
12844               }
12845               this.index++;
12846             }
12847             this.tokens.push({
12848               index: start,
12849               text: number,
12850               constant: true,
12851               value: Number(number)
12852             });
12853           },
12854
12855           readIdent: function() {
12856             var start = this.index;
12857             while (this.index < this.text.length) {
12858               var ch = this.text.charAt(this.index);
12859               if (!(this.isIdent(ch) || this.isNumber(ch))) {
12860                 break;
12861               }
12862               this.index++;
12863             }
12864             this.tokens.push({
12865               index: start,
12866               text: this.text.slice(start, this.index),
12867               identifier: true
12868             });
12869           },
12870
12871           readString: function(quote) {
12872             var start = this.index;
12873             this.index++;
12874             var string = '';
12875             var rawString = quote;
12876             var escape = false;
12877             while (this.index < this.text.length) {
12878               var ch = this.text.charAt(this.index);
12879               rawString += ch;
12880               if (escape) {
12881                 if (ch === 'u') {
12882                   var hex = this.text.substring(this.index + 1, this.index + 5);
12883                   if (!hex.match(/[\da-f]{4}/i)) {
12884                     this.throwError('Invalid unicode escape [\\u' + hex + ']');
12885                   }
12886                   this.index += 4;
12887                   string += String.fromCharCode(parseInt(hex, 16));
12888                 } else {
12889                   var rep = ESCAPE[ch];
12890                   string = string + (rep || ch);
12891                 }
12892                 escape = false;
12893               } else if (ch === '\\') {
12894                 escape = true;
12895               } else if (ch === quote) {
12896                 this.index++;
12897                 this.tokens.push({
12898                   index: start,
12899                   text: rawString,
12900                   constant: true,
12901                   value: string
12902                 });
12903                 return;
12904               } else {
12905                 string += ch;
12906               }
12907               this.index++;
12908             }
12909             this.throwError('Unterminated quote', start);
12910           }
12911         };
12912
12913         var AST = function(lexer, options) {
12914           this.lexer = lexer;
12915           this.options = options;
12916         };
12917
12918         AST.Program = 'Program';
12919         AST.ExpressionStatement = 'ExpressionStatement';
12920         AST.AssignmentExpression = 'AssignmentExpression';
12921         AST.ConditionalExpression = 'ConditionalExpression';
12922         AST.LogicalExpression = 'LogicalExpression';
12923         AST.BinaryExpression = 'BinaryExpression';
12924         AST.UnaryExpression = 'UnaryExpression';
12925         AST.CallExpression = 'CallExpression';
12926         AST.MemberExpression = 'MemberExpression';
12927         AST.Identifier = 'Identifier';
12928         AST.Literal = 'Literal';
12929         AST.ArrayExpression = 'ArrayExpression';
12930         AST.Property = 'Property';
12931         AST.ObjectExpression = 'ObjectExpression';
12932         AST.ThisExpression = 'ThisExpression';
12933
12934         // Internal use only
12935         AST.NGValueParameter = 'NGValueParameter';
12936
12937         AST.prototype = {
12938           ast: function(text) {
12939             this.text = text;
12940             this.tokens = this.lexer.lex(text);
12941
12942             var value = this.program();
12943
12944             if (this.tokens.length !== 0) {
12945               this.throwError('is an unexpected token', this.tokens[0]);
12946             }
12947
12948             return value;
12949           },
12950
12951           program: function() {
12952             var body = [];
12953             while (true) {
12954               if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12955                 body.push(this.expressionStatement());
12956               if (!this.expect(';')) {
12957                 return { type: AST.Program, body: body};
12958               }
12959             }
12960           },
12961
12962           expressionStatement: function() {
12963             return { type: AST.ExpressionStatement, expression: this.filterChain() };
12964           },
12965
12966           filterChain: function() {
12967             var left = this.expression();
12968             var token;
12969             while ((token = this.expect('|'))) {
12970               left = this.filter(left);
12971             }
12972             return left;
12973           },
12974
12975           expression: function() {
12976             return this.assignment();
12977           },
12978
12979           assignment: function() {
12980             var result = this.ternary();
12981             if (this.expect('=')) {
12982               result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12983             }
12984             return result;
12985           },
12986
12987           ternary: function() {
12988             var test = this.logicalOR();
12989             var alternate;
12990             var consequent;
12991             if (this.expect('?')) {
12992               alternate = this.expression();
12993               if (this.consume(':')) {
12994                 consequent = this.expression();
12995                 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12996               }
12997             }
12998             return test;
12999           },
13000
13001           logicalOR: function() {
13002             var left = this.logicalAND();
13003             while (this.expect('||')) {
13004               left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13005             }
13006             return left;
13007           },
13008
13009           logicalAND: function() {
13010             var left = this.equality();
13011             while (this.expect('&&')) {
13012               left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13013             }
13014             return left;
13015           },
13016
13017           equality: function() {
13018             var left = this.relational();
13019             var token;
13020             while ((token = this.expect('==','!=','===','!=='))) {
13021               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13022             }
13023             return left;
13024           },
13025
13026           relational: function() {
13027             var left = this.additive();
13028             var token;
13029             while ((token = this.expect('<', '>', '<=', '>='))) {
13030               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13031             }
13032             return left;
13033           },
13034
13035           additive: function() {
13036             var left = this.multiplicative();
13037             var token;
13038             while ((token = this.expect('+','-'))) {
13039               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13040             }
13041             return left;
13042           },
13043
13044           multiplicative: function() {
13045             var left = this.unary();
13046             var token;
13047             while ((token = this.expect('*','/','%'))) {
13048               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13049             }
13050             return left;
13051           },
13052
13053           unary: function() {
13054             var token;
13055             if ((token = this.expect('+', '-', '!'))) {
13056               return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13057             } else {
13058               return this.primary();
13059             }
13060           },
13061
13062           primary: function() {
13063             var primary;
13064             if (this.expect('(')) {
13065               primary = this.filterChain();
13066               this.consume(')');
13067             } else if (this.expect('[')) {
13068               primary = this.arrayDeclaration();
13069             } else if (this.expect('{')) {
13070               primary = this.object();
13071             } else if (this.constants.hasOwnProperty(this.peek().text)) {
13072               primary = copy(this.constants[this.consume().text]);
13073             } else if (this.peek().identifier) {
13074               primary = this.identifier();
13075             } else if (this.peek().constant) {
13076               primary = this.constant();
13077             } else {
13078               this.throwError('not a primary expression', this.peek());
13079             }
13080
13081             var next;
13082             while ((next = this.expect('(', '[', '.'))) {
13083               if (next.text === '(') {
13084                 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13085                 this.consume(')');
13086               } else if (next.text === '[') {
13087                 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13088                 this.consume(']');
13089               } else if (next.text === '.') {
13090                 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13091               } else {
13092                 this.throwError('IMPOSSIBLE');
13093               }
13094             }
13095             return primary;
13096           },
13097
13098           filter: function(baseExpression) {
13099             var args = [baseExpression];
13100             var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13101
13102             while (this.expect(':')) {
13103               args.push(this.expression());
13104             }
13105
13106             return result;
13107           },
13108
13109           parseArguments: function() {
13110             var args = [];
13111             if (this.peekToken().text !== ')') {
13112               do {
13113                 args.push(this.expression());
13114               } while (this.expect(','));
13115             }
13116             return args;
13117           },
13118
13119           identifier: function() {
13120             var token = this.consume();
13121             if (!token.identifier) {
13122               this.throwError('is not a valid identifier', token);
13123             }
13124             return { type: AST.Identifier, name: token.text };
13125           },
13126
13127           constant: function() {
13128             // TODO check that it is a constant
13129             return { type: AST.Literal, value: this.consume().value };
13130           },
13131
13132           arrayDeclaration: function() {
13133             var elements = [];
13134             if (this.peekToken().text !== ']') {
13135               do {
13136                 if (this.peek(']')) {
13137                   // Support trailing commas per ES5.1.
13138                   break;
13139                 }
13140                 elements.push(this.expression());
13141               } while (this.expect(','));
13142             }
13143             this.consume(']');
13144
13145             return { type: AST.ArrayExpression, elements: elements };
13146           },
13147
13148           object: function() {
13149             var properties = [], property;
13150             if (this.peekToken().text !== '}') {
13151               do {
13152                 if (this.peek('}')) {
13153                   // Support trailing commas per ES5.1.
13154                   break;
13155                 }
13156                 property = {type: AST.Property, kind: 'init'};
13157                 if (this.peek().constant) {
13158                   property.key = this.constant();
13159                 } else if (this.peek().identifier) {
13160                   property.key = this.identifier();
13161                 } else {
13162                   this.throwError("invalid key", this.peek());
13163                 }
13164                 this.consume(':');
13165                 property.value = this.expression();
13166                 properties.push(property);
13167               } while (this.expect(','));
13168             }
13169             this.consume('}');
13170
13171             return {type: AST.ObjectExpression, properties: properties };
13172           },
13173
13174           throwError: function(msg, token) {
13175             throw $parseMinErr('syntax',
13176                 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13177                   token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13178           },
13179
13180           consume: function(e1) {
13181             if (this.tokens.length === 0) {
13182               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13183             }
13184
13185             var token = this.expect(e1);
13186             if (!token) {
13187               this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13188             }
13189             return token;
13190           },
13191
13192           peekToken: function() {
13193             if (this.tokens.length === 0) {
13194               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13195             }
13196             return this.tokens[0];
13197           },
13198
13199           peek: function(e1, e2, e3, e4) {
13200             return this.peekAhead(0, e1, e2, e3, e4);
13201           },
13202
13203           peekAhead: function(i, e1, e2, e3, e4) {
13204             if (this.tokens.length > i) {
13205               var token = this.tokens[i];
13206               var t = token.text;
13207               if (t === e1 || t === e2 || t === e3 || t === e4 ||
13208                   (!e1 && !e2 && !e3 && !e4)) {
13209                 return token;
13210               }
13211             }
13212             return false;
13213           },
13214
13215           expect: function(e1, e2, e3, e4) {
13216             var token = this.peek(e1, e2, e3, e4);
13217             if (token) {
13218               this.tokens.shift();
13219               return token;
13220             }
13221             return false;
13222           },
13223
13224
13225           /* `undefined` is not a constant, it is an identifier,
13226            * but using it as an identifier is not supported
13227            */
13228           constants: {
13229             'true': { type: AST.Literal, value: true },
13230             'false': { type: AST.Literal, value: false },
13231             'null': { type: AST.Literal, value: null },
13232             'undefined': {type: AST.Literal, value: undefined },
13233             'this': {type: AST.ThisExpression }
13234           }
13235         };
13236
13237         function ifDefined(v, d) {
13238           return typeof v !== 'undefined' ? v : d;
13239         }
13240
13241         function plusFn(l, r) {
13242           if (typeof l === 'undefined') return r;
13243           if (typeof r === 'undefined') return l;
13244           return l + r;
13245         }
13246
13247         function isStateless($filter, filterName) {
13248           var fn = $filter(filterName);
13249           return !fn.$stateful;
13250         }
13251
13252         function findConstantAndWatchExpressions(ast, $filter) {
13253           var allConstants;
13254           var argsToWatch;
13255           switch (ast.type) {
13256           case AST.Program:
13257             allConstants = true;
13258             forEach(ast.body, function(expr) {
13259               findConstantAndWatchExpressions(expr.expression, $filter);
13260               allConstants = allConstants && expr.expression.constant;
13261             });
13262             ast.constant = allConstants;
13263             break;
13264           case AST.Literal:
13265             ast.constant = true;
13266             ast.toWatch = [];
13267             break;
13268           case AST.UnaryExpression:
13269             findConstantAndWatchExpressions(ast.argument, $filter);
13270             ast.constant = ast.argument.constant;
13271             ast.toWatch = ast.argument.toWatch;
13272             break;
13273           case AST.BinaryExpression:
13274             findConstantAndWatchExpressions(ast.left, $filter);
13275             findConstantAndWatchExpressions(ast.right, $filter);
13276             ast.constant = ast.left.constant && ast.right.constant;
13277             ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13278             break;
13279           case AST.LogicalExpression:
13280             findConstantAndWatchExpressions(ast.left, $filter);
13281             findConstantAndWatchExpressions(ast.right, $filter);
13282             ast.constant = ast.left.constant && ast.right.constant;
13283             ast.toWatch = ast.constant ? [] : [ast];
13284             break;
13285           case AST.ConditionalExpression:
13286             findConstantAndWatchExpressions(ast.test, $filter);
13287             findConstantAndWatchExpressions(ast.alternate, $filter);
13288             findConstantAndWatchExpressions(ast.consequent, $filter);
13289             ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13290             ast.toWatch = ast.constant ? [] : [ast];
13291             break;
13292           case AST.Identifier:
13293             ast.constant = false;
13294             ast.toWatch = [ast];
13295             break;
13296           case AST.MemberExpression:
13297             findConstantAndWatchExpressions(ast.object, $filter);
13298             if (ast.computed) {
13299               findConstantAndWatchExpressions(ast.property, $filter);
13300             }
13301             ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13302             ast.toWatch = [ast];
13303             break;
13304           case AST.CallExpression:
13305             allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13306             argsToWatch = [];
13307             forEach(ast.arguments, function(expr) {
13308               findConstantAndWatchExpressions(expr, $filter);
13309               allConstants = allConstants && expr.constant;
13310               if (!expr.constant) {
13311                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13312               }
13313             });
13314             ast.constant = allConstants;
13315             ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13316             break;
13317           case AST.AssignmentExpression:
13318             findConstantAndWatchExpressions(ast.left, $filter);
13319             findConstantAndWatchExpressions(ast.right, $filter);
13320             ast.constant = ast.left.constant && ast.right.constant;
13321             ast.toWatch = [ast];
13322             break;
13323           case AST.ArrayExpression:
13324             allConstants = true;
13325             argsToWatch = [];
13326             forEach(ast.elements, function(expr) {
13327               findConstantAndWatchExpressions(expr, $filter);
13328               allConstants = allConstants && expr.constant;
13329               if (!expr.constant) {
13330                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13331               }
13332             });
13333             ast.constant = allConstants;
13334             ast.toWatch = argsToWatch;
13335             break;
13336           case AST.ObjectExpression:
13337             allConstants = true;
13338             argsToWatch = [];
13339             forEach(ast.properties, function(property) {
13340               findConstantAndWatchExpressions(property.value, $filter);
13341               allConstants = allConstants && property.value.constant;
13342               if (!property.value.constant) {
13343                 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13344               }
13345             });
13346             ast.constant = allConstants;
13347             ast.toWatch = argsToWatch;
13348             break;
13349           case AST.ThisExpression:
13350             ast.constant = false;
13351             ast.toWatch = [];
13352             break;
13353           }
13354         }
13355
13356         function getInputs(body) {
13357           if (body.length != 1) return;
13358           var lastExpression = body[0].expression;
13359           var candidate = lastExpression.toWatch;
13360           if (candidate.length !== 1) return candidate;
13361           return candidate[0] !== lastExpression ? candidate : undefined;
13362         }
13363
13364         function isAssignable(ast) {
13365           return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13366         }
13367
13368         function assignableAST(ast) {
13369           if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13370             return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13371           }
13372         }
13373
13374         function isLiteral(ast) {
13375           return ast.body.length === 0 ||
13376               ast.body.length === 1 && (
13377               ast.body[0].expression.type === AST.Literal ||
13378               ast.body[0].expression.type === AST.ArrayExpression ||
13379               ast.body[0].expression.type === AST.ObjectExpression);
13380         }
13381
13382         function isConstant(ast) {
13383           return ast.constant;
13384         }
13385
13386         function ASTCompiler(astBuilder, $filter) {
13387           this.astBuilder = astBuilder;
13388           this.$filter = $filter;
13389         }
13390
13391         ASTCompiler.prototype = {
13392           compile: function(expression, expensiveChecks) {
13393             var self = this;
13394             var ast = this.astBuilder.ast(expression);
13395             this.state = {
13396               nextId: 0,
13397               filters: {},
13398               expensiveChecks: expensiveChecks,
13399               fn: {vars: [], body: [], own: {}},
13400               assign: {vars: [], body: [], own: {}},
13401               inputs: []
13402             };
13403             findConstantAndWatchExpressions(ast, self.$filter);
13404             var extra = '';
13405             var assignable;
13406             this.stage = 'assign';
13407             if ((assignable = assignableAST(ast))) {
13408               this.state.computing = 'assign';
13409               var result = this.nextId();
13410               this.recurse(assignable, result);
13411               this.return_(result);
13412               extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13413             }
13414             var toWatch = getInputs(ast.body);
13415             self.stage = 'inputs';
13416             forEach(toWatch, function(watch, key) {
13417               var fnKey = 'fn' + key;
13418               self.state[fnKey] = {vars: [], body: [], own: {}};
13419               self.state.computing = fnKey;
13420               var intoId = self.nextId();
13421               self.recurse(watch, intoId);
13422               self.return_(intoId);
13423               self.state.inputs.push(fnKey);
13424               watch.watchId = key;
13425             });
13426             this.state.computing = 'fn';
13427             this.stage = 'main';
13428             this.recurse(ast);
13429             var fnString =
13430               // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13431               // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13432               '"' + this.USE + ' ' + this.STRICT + '";\n' +
13433               this.filterPrefix() +
13434               'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13435               extra +
13436               this.watchFns() +
13437               'return fn;';
13438
13439             /* jshint -W054 */
13440             var fn = (new Function('$filter',
13441                 'ensureSafeMemberName',
13442                 'ensureSafeObject',
13443                 'ensureSafeFunction',
13444                 'getStringValue',
13445                 'ensureSafeAssignContext',
13446                 'ifDefined',
13447                 'plus',
13448                 'text',
13449                 fnString))(
13450                   this.$filter,
13451                   ensureSafeMemberName,
13452                   ensureSafeObject,
13453                   ensureSafeFunction,
13454                   getStringValue,
13455                   ensureSafeAssignContext,
13456                   ifDefined,
13457                   plusFn,
13458                   expression);
13459             /* jshint +W054 */
13460             this.state = this.stage = undefined;
13461             fn.literal = isLiteral(ast);
13462             fn.constant = isConstant(ast);
13463             return fn;
13464           },
13465
13466           USE: 'use',
13467
13468           STRICT: 'strict',
13469
13470           watchFns: function() {
13471             var result = [];
13472             var fns = this.state.inputs;
13473             var self = this;
13474             forEach(fns, function(name) {
13475               result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13476             });
13477             if (fns.length) {
13478               result.push('fn.inputs=[' + fns.join(',') + '];');
13479             }
13480             return result.join('');
13481           },
13482
13483           generateFunction: function(name, params) {
13484             return 'function(' + params + '){' +
13485                 this.varsPrefix(name) +
13486                 this.body(name) +
13487                 '};';
13488           },
13489
13490           filterPrefix: function() {
13491             var parts = [];
13492             var self = this;
13493             forEach(this.state.filters, function(id, filter) {
13494               parts.push(id + '=$filter(' + self.escape(filter) + ')');
13495             });
13496             if (parts.length) return 'var ' + parts.join(',') + ';';
13497             return '';
13498           },
13499
13500           varsPrefix: function(section) {
13501             return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13502           },
13503
13504           body: function(section) {
13505             return this.state[section].body.join('');
13506           },
13507
13508           recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13509             var left, right, self = this, args, expression;
13510             recursionFn = recursionFn || noop;
13511             if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13512               intoId = intoId || this.nextId();
13513               this.if_('i',
13514                 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13515                 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13516               );
13517               return;
13518             }
13519             switch (ast.type) {
13520             case AST.Program:
13521               forEach(ast.body, function(expression, pos) {
13522                 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13523                 if (pos !== ast.body.length - 1) {
13524                   self.current().body.push(right, ';');
13525                 } else {
13526                   self.return_(right);
13527                 }
13528               });
13529               break;
13530             case AST.Literal:
13531               expression = this.escape(ast.value);
13532               this.assign(intoId, expression);
13533               recursionFn(expression);
13534               break;
13535             case AST.UnaryExpression:
13536               this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13537               expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13538               this.assign(intoId, expression);
13539               recursionFn(expression);
13540               break;
13541             case AST.BinaryExpression:
13542               this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13543               this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13544               if (ast.operator === '+') {
13545                 expression = this.plus(left, right);
13546               } else if (ast.operator === '-') {
13547                 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13548               } else {
13549                 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13550               }
13551               this.assign(intoId, expression);
13552               recursionFn(expression);
13553               break;
13554             case AST.LogicalExpression:
13555               intoId = intoId || this.nextId();
13556               self.recurse(ast.left, intoId);
13557               self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13558               recursionFn(intoId);
13559               break;
13560             case AST.ConditionalExpression:
13561               intoId = intoId || this.nextId();
13562               self.recurse(ast.test, intoId);
13563               self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13564               recursionFn(intoId);
13565               break;
13566             case AST.Identifier:
13567               intoId = intoId || this.nextId();
13568               if (nameId) {
13569                 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13570                 nameId.computed = false;
13571                 nameId.name = ast.name;
13572               }
13573               ensureSafeMemberName(ast.name);
13574               self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13575                 function() {
13576                   self.if_(self.stage === 'inputs' || 's', function() {
13577                     if (create && create !== 1) {
13578                       self.if_(
13579                         self.not(self.nonComputedMember('s', ast.name)),
13580                         self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13581                     }
13582                     self.assign(intoId, self.nonComputedMember('s', ast.name));
13583                   });
13584                 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13585                 );
13586               if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13587                 self.addEnsureSafeObject(intoId);
13588               }
13589               recursionFn(intoId);
13590               break;
13591             case AST.MemberExpression:
13592               left = nameId && (nameId.context = this.nextId()) || this.nextId();
13593               intoId = intoId || this.nextId();
13594               self.recurse(ast.object, left, undefined, function() {
13595                 self.if_(self.notNull(left), function() {
13596                   if (ast.computed) {
13597                     right = self.nextId();
13598                     self.recurse(ast.property, right);
13599                     self.getStringValue(right);
13600                     self.addEnsureSafeMemberName(right);
13601                     if (create && create !== 1) {
13602                       self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13603                     }
13604                     expression = self.ensureSafeObject(self.computedMember(left, right));
13605                     self.assign(intoId, expression);
13606                     if (nameId) {
13607                       nameId.computed = true;
13608                       nameId.name = right;
13609                     }
13610                   } else {
13611                     ensureSafeMemberName(ast.property.name);
13612                     if (create && create !== 1) {
13613                       self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13614                     }
13615                     expression = self.nonComputedMember(left, ast.property.name);
13616                     if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13617                       expression = self.ensureSafeObject(expression);
13618                     }
13619                     self.assign(intoId, expression);
13620                     if (nameId) {
13621                       nameId.computed = false;
13622                       nameId.name = ast.property.name;
13623                     }
13624                   }
13625                 }, function() {
13626                   self.assign(intoId, 'undefined');
13627                 });
13628                 recursionFn(intoId);
13629               }, !!create);
13630               break;
13631             case AST.CallExpression:
13632               intoId = intoId || this.nextId();
13633               if (ast.filter) {
13634                 right = self.filter(ast.callee.name);
13635                 args = [];
13636                 forEach(ast.arguments, function(expr) {
13637                   var argument = self.nextId();
13638                   self.recurse(expr, argument);
13639                   args.push(argument);
13640                 });
13641                 expression = right + '(' + args.join(',') + ')';
13642                 self.assign(intoId, expression);
13643                 recursionFn(intoId);
13644               } else {
13645                 right = self.nextId();
13646                 left = {};
13647                 args = [];
13648                 self.recurse(ast.callee, right, left, function() {
13649                   self.if_(self.notNull(right), function() {
13650                     self.addEnsureSafeFunction(right);
13651                     forEach(ast.arguments, function(expr) {
13652                       self.recurse(expr, self.nextId(), undefined, function(argument) {
13653                         args.push(self.ensureSafeObject(argument));
13654                       });
13655                     });
13656                     if (left.name) {
13657                       if (!self.state.expensiveChecks) {
13658                         self.addEnsureSafeObject(left.context);
13659                       }
13660                       expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13661                     } else {
13662                       expression = right + '(' + args.join(',') + ')';
13663                     }
13664                     expression = self.ensureSafeObject(expression);
13665                     self.assign(intoId, expression);
13666                   }, function() {
13667                     self.assign(intoId, 'undefined');
13668                   });
13669                   recursionFn(intoId);
13670                 });
13671               }
13672               break;
13673             case AST.AssignmentExpression:
13674               right = this.nextId();
13675               left = {};
13676               if (!isAssignable(ast.left)) {
13677                 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13678               }
13679               this.recurse(ast.left, undefined, left, function() {
13680                 self.if_(self.notNull(left.context), function() {
13681                   self.recurse(ast.right, right);
13682                   self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13683                   self.addEnsureSafeAssignContext(left.context);
13684                   expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13685                   self.assign(intoId, expression);
13686                   recursionFn(intoId || expression);
13687                 });
13688               }, 1);
13689               break;
13690             case AST.ArrayExpression:
13691               args = [];
13692               forEach(ast.elements, function(expr) {
13693                 self.recurse(expr, self.nextId(), undefined, function(argument) {
13694                   args.push(argument);
13695                 });
13696               });
13697               expression = '[' + args.join(',') + ']';
13698               this.assign(intoId, expression);
13699               recursionFn(expression);
13700               break;
13701             case AST.ObjectExpression:
13702               args = [];
13703               forEach(ast.properties, function(property) {
13704                 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13705                   args.push(self.escape(
13706                       property.key.type === AST.Identifier ? property.key.name :
13707                         ('' + property.key.value)) +
13708                       ':' + expr);
13709                 });
13710               });
13711               expression = '{' + args.join(',') + '}';
13712               this.assign(intoId, expression);
13713               recursionFn(expression);
13714               break;
13715             case AST.ThisExpression:
13716               this.assign(intoId, 's');
13717               recursionFn('s');
13718               break;
13719             case AST.NGValueParameter:
13720               this.assign(intoId, 'v');
13721               recursionFn('v');
13722               break;
13723             }
13724           },
13725
13726           getHasOwnProperty: function(element, property) {
13727             var key = element + '.' + property;
13728             var own = this.current().own;
13729             if (!own.hasOwnProperty(key)) {
13730               own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13731             }
13732             return own[key];
13733           },
13734
13735           assign: function(id, value) {
13736             if (!id) return;
13737             this.current().body.push(id, '=', value, ';');
13738             return id;
13739           },
13740
13741           filter: function(filterName) {
13742             if (!this.state.filters.hasOwnProperty(filterName)) {
13743               this.state.filters[filterName] = this.nextId(true);
13744             }
13745             return this.state.filters[filterName];
13746           },
13747
13748           ifDefined: function(id, defaultValue) {
13749             return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13750           },
13751
13752           plus: function(left, right) {
13753             return 'plus(' + left + ',' + right + ')';
13754           },
13755
13756           return_: function(id) {
13757             this.current().body.push('return ', id, ';');
13758           },
13759
13760           if_: function(test, alternate, consequent) {
13761             if (test === true) {
13762               alternate();
13763             } else {
13764               var body = this.current().body;
13765               body.push('if(', test, '){');
13766               alternate();
13767               body.push('}');
13768               if (consequent) {
13769                 body.push('else{');
13770                 consequent();
13771                 body.push('}');
13772               }
13773             }
13774           },
13775
13776           not: function(expression) {
13777             return '!(' + expression + ')';
13778           },
13779
13780           notNull: function(expression) {
13781             return expression + '!=null';
13782           },
13783
13784           nonComputedMember: function(left, right) {
13785             return left + '.' + right;
13786           },
13787
13788           computedMember: function(left, right) {
13789             return left + '[' + right + ']';
13790           },
13791
13792           member: function(left, right, computed) {
13793             if (computed) return this.computedMember(left, right);
13794             return this.nonComputedMember(left, right);
13795           },
13796
13797           addEnsureSafeObject: function(item) {
13798             this.current().body.push(this.ensureSafeObject(item), ';');
13799           },
13800
13801           addEnsureSafeMemberName: function(item) {
13802             this.current().body.push(this.ensureSafeMemberName(item), ';');
13803           },
13804
13805           addEnsureSafeFunction: function(item) {
13806             this.current().body.push(this.ensureSafeFunction(item), ';');
13807           },
13808
13809           addEnsureSafeAssignContext: function(item) {
13810             this.current().body.push(this.ensureSafeAssignContext(item), ';');
13811           },
13812
13813           ensureSafeObject: function(item) {
13814             return 'ensureSafeObject(' + item + ',text)';
13815           },
13816
13817           ensureSafeMemberName: function(item) {
13818             return 'ensureSafeMemberName(' + item + ',text)';
13819           },
13820
13821           ensureSafeFunction: function(item) {
13822             return 'ensureSafeFunction(' + item + ',text)';
13823           },
13824
13825           getStringValue: function(item) {
13826             this.assign(item, 'getStringValue(' + item + ',text)');
13827           },
13828
13829           ensureSafeAssignContext: function(item) {
13830             return 'ensureSafeAssignContext(' + item + ',text)';
13831           },
13832
13833           lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13834             var self = this;
13835             return function() {
13836               self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13837             };
13838           },
13839
13840           lazyAssign: function(id, value) {
13841             var self = this;
13842             return function() {
13843               self.assign(id, value);
13844             };
13845           },
13846
13847           stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13848
13849           stringEscapeFn: function(c) {
13850             return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13851           },
13852
13853           escape: function(value) {
13854             if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13855             if (isNumber(value)) return value.toString();
13856             if (value === true) return 'true';
13857             if (value === false) return 'false';
13858             if (value === null) return 'null';
13859             if (typeof value === 'undefined') return 'undefined';
13860
13861             throw $parseMinErr('esc', 'IMPOSSIBLE');
13862           },
13863
13864           nextId: function(skip, init) {
13865             var id = 'v' + (this.state.nextId++);
13866             if (!skip) {
13867               this.current().vars.push(id + (init ? '=' + init : ''));
13868             }
13869             return id;
13870           },
13871
13872           current: function() {
13873             return this.state[this.state.computing];
13874           }
13875         };
13876
13877
13878         function ASTInterpreter(astBuilder, $filter) {
13879           this.astBuilder = astBuilder;
13880           this.$filter = $filter;
13881         }
13882
13883         ASTInterpreter.prototype = {
13884           compile: function(expression, expensiveChecks) {
13885             var self = this;
13886             var ast = this.astBuilder.ast(expression);
13887             this.expression = expression;
13888             this.expensiveChecks = expensiveChecks;
13889             findConstantAndWatchExpressions(ast, self.$filter);
13890             var assignable;
13891             var assign;
13892             if ((assignable = assignableAST(ast))) {
13893               assign = this.recurse(assignable);
13894             }
13895             var toWatch = getInputs(ast.body);
13896             var inputs;
13897             if (toWatch) {
13898               inputs = [];
13899               forEach(toWatch, function(watch, key) {
13900                 var input = self.recurse(watch);
13901                 watch.input = input;
13902                 inputs.push(input);
13903                 watch.watchId = key;
13904               });
13905             }
13906             var expressions = [];
13907             forEach(ast.body, function(expression) {
13908               expressions.push(self.recurse(expression.expression));
13909             });
13910             var fn = ast.body.length === 0 ? function() {} :
13911                      ast.body.length === 1 ? expressions[0] :
13912                      function(scope, locals) {
13913                        var lastValue;
13914                        forEach(expressions, function(exp) {
13915                          lastValue = exp(scope, locals);
13916                        });
13917                        return lastValue;
13918                      };
13919             if (assign) {
13920               fn.assign = function(scope, value, locals) {
13921                 return assign(scope, locals, value);
13922               };
13923             }
13924             if (inputs) {
13925               fn.inputs = inputs;
13926             }
13927             fn.literal = isLiteral(ast);
13928             fn.constant = isConstant(ast);
13929             return fn;
13930           },
13931
13932           recurse: function(ast, context, create) {
13933             var left, right, self = this, args, expression;
13934             if (ast.input) {
13935               return this.inputs(ast.input, ast.watchId);
13936             }
13937             switch (ast.type) {
13938             case AST.Literal:
13939               return this.value(ast.value, context);
13940             case AST.UnaryExpression:
13941               right = this.recurse(ast.argument);
13942               return this['unary' + ast.operator](right, context);
13943             case AST.BinaryExpression:
13944               left = this.recurse(ast.left);
13945               right = this.recurse(ast.right);
13946               return this['binary' + ast.operator](left, right, context);
13947             case AST.LogicalExpression:
13948               left = this.recurse(ast.left);
13949               right = this.recurse(ast.right);
13950               return this['binary' + ast.operator](left, right, context);
13951             case AST.ConditionalExpression:
13952               return this['ternary?:'](
13953                 this.recurse(ast.test),
13954                 this.recurse(ast.alternate),
13955                 this.recurse(ast.consequent),
13956                 context
13957               );
13958             case AST.Identifier:
13959               ensureSafeMemberName(ast.name, self.expression);
13960               return self.identifier(ast.name,
13961                                      self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13962                                      context, create, self.expression);
13963             case AST.MemberExpression:
13964               left = this.recurse(ast.object, false, !!create);
13965               if (!ast.computed) {
13966                 ensureSafeMemberName(ast.property.name, self.expression);
13967                 right = ast.property.name;
13968               }
13969               if (ast.computed) right = this.recurse(ast.property);
13970               return ast.computed ?
13971                 this.computedMember(left, right, context, create, self.expression) :
13972                 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13973             case AST.CallExpression:
13974               args = [];
13975               forEach(ast.arguments, function(expr) {
13976                 args.push(self.recurse(expr));
13977               });
13978               if (ast.filter) right = this.$filter(ast.callee.name);
13979               if (!ast.filter) right = this.recurse(ast.callee, true);
13980               return ast.filter ?
13981                 function(scope, locals, assign, inputs) {
13982                   var values = [];
13983                   for (var i = 0; i < args.length; ++i) {
13984                     values.push(args[i](scope, locals, assign, inputs));
13985                   }
13986                   var value = right.apply(undefined, values, inputs);
13987                   return context ? {context: undefined, name: undefined, value: value} : value;
13988                 } :
13989                 function(scope, locals, assign, inputs) {
13990                   var rhs = right(scope, locals, assign, inputs);
13991                   var value;
13992                   if (rhs.value != null) {
13993                     ensureSafeObject(rhs.context, self.expression);
13994                     ensureSafeFunction(rhs.value, self.expression);
13995                     var values = [];
13996                     for (var i = 0; i < args.length; ++i) {
13997                       values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13998                     }
13999                     value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14000                   }
14001                   return context ? {value: value} : value;
14002                 };
14003             case AST.AssignmentExpression:
14004               left = this.recurse(ast.left, true, 1);
14005               right = this.recurse(ast.right);
14006               return function(scope, locals, assign, inputs) {
14007                 var lhs = left(scope, locals, assign, inputs);
14008                 var rhs = right(scope, locals, assign, inputs);
14009                 ensureSafeObject(lhs.value, self.expression);
14010                 ensureSafeAssignContext(lhs.context);
14011                 lhs.context[lhs.name] = rhs;
14012                 return context ? {value: rhs} : rhs;
14013               };
14014             case AST.ArrayExpression:
14015               args = [];
14016               forEach(ast.elements, function(expr) {
14017                 args.push(self.recurse(expr));
14018               });
14019               return function(scope, locals, assign, inputs) {
14020                 var value = [];
14021                 for (var i = 0; i < args.length; ++i) {
14022                   value.push(args[i](scope, locals, assign, inputs));
14023                 }
14024                 return context ? {value: value} : value;
14025               };
14026             case AST.ObjectExpression:
14027               args = [];
14028               forEach(ast.properties, function(property) {
14029                 args.push({key: property.key.type === AST.Identifier ?
14030                                 property.key.name :
14031                                 ('' + property.key.value),
14032                            value: self.recurse(property.value)
14033                 });
14034               });
14035               return function(scope, locals, assign, inputs) {
14036                 var value = {};
14037                 for (var i = 0; i < args.length; ++i) {
14038                   value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14039                 }
14040                 return context ? {value: value} : value;
14041               };
14042             case AST.ThisExpression:
14043               return function(scope) {
14044                 return context ? {value: scope} : scope;
14045               };
14046             case AST.NGValueParameter:
14047               return function(scope, locals, assign, inputs) {
14048                 return context ? {value: assign} : assign;
14049               };
14050             }
14051           },
14052
14053           'unary+': function(argument, context) {
14054             return function(scope, locals, assign, inputs) {
14055               var arg = argument(scope, locals, assign, inputs);
14056               if (isDefined(arg)) {
14057                 arg = +arg;
14058               } else {
14059                 arg = 0;
14060               }
14061               return context ? {value: arg} : arg;
14062             };
14063           },
14064           'unary-': function(argument, context) {
14065             return function(scope, locals, assign, inputs) {
14066               var arg = argument(scope, locals, assign, inputs);
14067               if (isDefined(arg)) {
14068                 arg = -arg;
14069               } else {
14070                 arg = 0;
14071               }
14072               return context ? {value: arg} : arg;
14073             };
14074           },
14075           'unary!': function(argument, context) {
14076             return function(scope, locals, assign, inputs) {
14077               var arg = !argument(scope, locals, assign, inputs);
14078               return context ? {value: arg} : arg;
14079             };
14080           },
14081           'binary+': function(left, right, context) {
14082             return function(scope, locals, assign, inputs) {
14083               var lhs = left(scope, locals, assign, inputs);
14084               var rhs = right(scope, locals, assign, inputs);
14085               var arg = plusFn(lhs, rhs);
14086               return context ? {value: arg} : arg;
14087             };
14088           },
14089           'binary-': function(left, right, context) {
14090             return function(scope, locals, assign, inputs) {
14091               var lhs = left(scope, locals, assign, inputs);
14092               var rhs = right(scope, locals, assign, inputs);
14093               var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14094               return context ? {value: arg} : arg;
14095             };
14096           },
14097           'binary*': function(left, right, context) {
14098             return function(scope, locals, assign, inputs) {
14099               var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14100               return context ? {value: arg} : arg;
14101             };
14102           },
14103           'binary/': function(left, right, context) {
14104             return function(scope, locals, assign, inputs) {
14105               var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14106               return context ? {value: arg} : arg;
14107             };
14108           },
14109           'binary%': function(left, right, context) {
14110             return function(scope, locals, assign, inputs) {
14111               var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14112               return context ? {value: arg} : arg;
14113             };
14114           },
14115           'binary===': function(left, right, context) {
14116             return function(scope, locals, assign, inputs) {
14117               var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14118               return context ? {value: arg} : arg;
14119             };
14120           },
14121           'binary!==': function(left, right, context) {
14122             return function(scope, locals, assign, inputs) {
14123               var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14124               return context ? {value: arg} : arg;
14125             };
14126           },
14127           'binary==': function(left, right, context) {
14128             return function(scope, locals, assign, inputs) {
14129               var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14130               return context ? {value: arg} : arg;
14131             };
14132           },
14133           'binary!=': function(left, right, context) {
14134             return function(scope, locals, assign, inputs) {
14135               var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14136               return context ? {value: arg} : arg;
14137             };
14138           },
14139           'binary<': function(left, right, context) {
14140             return function(scope, locals, assign, inputs) {
14141               var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14142               return context ? {value: arg} : arg;
14143             };
14144           },
14145           'binary>': function(left, right, context) {
14146             return function(scope, locals, assign, inputs) {
14147               var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14148               return context ? {value: arg} : arg;
14149             };
14150           },
14151           'binary<=': function(left, right, context) {
14152             return function(scope, locals, assign, inputs) {
14153               var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14154               return context ? {value: arg} : arg;
14155             };
14156           },
14157           'binary>=': function(left, right, context) {
14158             return function(scope, locals, assign, inputs) {
14159               var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14160               return context ? {value: arg} : arg;
14161             };
14162           },
14163           'binary&&': function(left, right, context) {
14164             return function(scope, locals, assign, inputs) {
14165               var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14166               return context ? {value: arg} : arg;
14167             };
14168           },
14169           'binary||': function(left, right, context) {
14170             return function(scope, locals, assign, inputs) {
14171               var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14172               return context ? {value: arg} : arg;
14173             };
14174           },
14175           'ternary?:': function(test, alternate, consequent, context) {
14176             return function(scope, locals, assign, inputs) {
14177               var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14178               return context ? {value: arg} : arg;
14179             };
14180           },
14181           value: function(value, context) {
14182             return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14183           },
14184           identifier: function(name, expensiveChecks, context, create, expression) {
14185             return function(scope, locals, assign, inputs) {
14186               var base = locals && (name in locals) ? locals : scope;
14187               if (create && create !== 1 && base && !(base[name])) {
14188                 base[name] = {};
14189               }
14190               var value = base ? base[name] : undefined;
14191               if (expensiveChecks) {
14192                 ensureSafeObject(value, expression);
14193               }
14194               if (context) {
14195                 return {context: base, name: name, value: value};
14196               } else {
14197                 return value;
14198               }
14199             };
14200           },
14201           computedMember: function(left, right, context, create, expression) {
14202             return function(scope, locals, assign, inputs) {
14203               var lhs = left(scope, locals, assign, inputs);
14204               var rhs;
14205               var value;
14206               if (lhs != null) {
14207                 rhs = right(scope, locals, assign, inputs);
14208                 rhs = getStringValue(rhs);
14209                 ensureSafeMemberName(rhs, expression);
14210                 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14211                   lhs[rhs] = {};
14212                 }
14213                 value = lhs[rhs];
14214                 ensureSafeObject(value, expression);
14215               }
14216               if (context) {
14217                 return {context: lhs, name: rhs, value: value};
14218               } else {
14219                 return value;
14220               }
14221             };
14222           },
14223           nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14224             return function(scope, locals, assign, inputs) {
14225               var lhs = left(scope, locals, assign, inputs);
14226               if (create && create !== 1 && lhs && !(lhs[right])) {
14227                 lhs[right] = {};
14228               }
14229               var value = lhs != null ? lhs[right] : undefined;
14230               if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14231                 ensureSafeObject(value, expression);
14232               }
14233               if (context) {
14234                 return {context: lhs, name: right, value: value};
14235               } else {
14236                 return value;
14237               }
14238             };
14239           },
14240           inputs: function(input, watchId) {
14241             return function(scope, value, locals, inputs) {
14242               if (inputs) return inputs[watchId];
14243               return input(scope, value, locals);
14244             };
14245           }
14246         };
14247
14248         /**
14249          * @constructor
14250          */
14251         var Parser = function(lexer, $filter, options) {
14252           this.lexer = lexer;
14253           this.$filter = $filter;
14254           this.options = options;
14255           this.ast = new AST(this.lexer);
14256           this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14257                                            new ASTCompiler(this.ast, $filter);
14258         };
14259
14260         Parser.prototype = {
14261           constructor: Parser,
14262
14263           parse: function(text) {
14264             return this.astCompiler.compile(text, this.options.expensiveChecks);
14265           }
14266         };
14267
14268         var getterFnCacheDefault = createMap();
14269         var getterFnCacheExpensive = createMap();
14270
14271         function isPossiblyDangerousMemberName(name) {
14272           return name == 'constructor';
14273         }
14274
14275         var objectValueOf = Object.prototype.valueOf;
14276
14277         function getValueOf(value) {
14278           return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14279         }
14280
14281         ///////////////////////////////////
14282
14283         /**
14284          * @ngdoc service
14285          * @name $parse
14286          * @kind function
14287          *
14288          * @description
14289          *
14290          * Converts Angular {@link guide/expression expression} into a function.
14291          *
14292          * ```js
14293          *   var getter = $parse('user.name');
14294          *   var setter = getter.assign;
14295          *   var context = {user:{name:'angular'}};
14296          *   var locals = {user:{name:'local'}};
14297          *
14298          *   expect(getter(context)).toEqual('angular');
14299          *   setter(context, 'newValue');
14300          *   expect(context.user.name).toEqual('newValue');
14301          *   expect(getter(context, locals)).toEqual('local');
14302          * ```
14303          *
14304          *
14305          * @param {string} expression String expression to compile.
14306          * @returns {function(context, locals)} a function which represents the compiled expression:
14307          *
14308          *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14309          *      are evaluated against (typically a scope object).
14310          *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14311          *      `context`.
14312          *
14313          *    The returned function also has the following properties:
14314          *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14315          *        literal.
14316          *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14317          *        constant literals.
14318          *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14319          *        set to a function to change its value on the given context.
14320          *
14321          */
14322
14323
14324         /**
14325          * @ngdoc provider
14326          * @name $parseProvider
14327          *
14328          * @description
14329          * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14330          *  service.
14331          */
14332         function $ParseProvider() {
14333           var cacheDefault = createMap();
14334           var cacheExpensive = createMap();
14335
14336           this.$get = ['$filter', function($filter) {
14337             var noUnsafeEval = csp().noUnsafeEval;
14338             var $parseOptions = {
14339                   csp: noUnsafeEval,
14340                   expensiveChecks: false
14341                 },
14342                 $parseOptionsExpensive = {
14343                   csp: noUnsafeEval,
14344                   expensiveChecks: true
14345                 };
14346
14347             return function $parse(exp, interceptorFn, expensiveChecks) {
14348               var parsedExpression, oneTime, cacheKey;
14349
14350               switch (typeof exp) {
14351                 case 'string':
14352                   exp = exp.trim();
14353                   cacheKey = exp;
14354
14355                   var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14356                   parsedExpression = cache[cacheKey];
14357
14358                   if (!parsedExpression) {
14359                     if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14360                       oneTime = true;
14361                       exp = exp.substring(2);
14362                     }
14363                     var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14364                     var lexer = new Lexer(parseOptions);
14365                     var parser = new Parser(lexer, $filter, parseOptions);
14366                     parsedExpression = parser.parse(exp);
14367                     if (parsedExpression.constant) {
14368                       parsedExpression.$$watchDelegate = constantWatchDelegate;
14369                     } else if (oneTime) {
14370                       parsedExpression.$$watchDelegate = parsedExpression.literal ?
14371                           oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14372                     } else if (parsedExpression.inputs) {
14373                       parsedExpression.$$watchDelegate = inputsWatchDelegate;
14374                     }
14375                     cache[cacheKey] = parsedExpression;
14376                   }
14377                   return addInterceptor(parsedExpression, interceptorFn);
14378
14379                 case 'function':
14380                   return addInterceptor(exp, interceptorFn);
14381
14382                 default:
14383                   return noop;
14384               }
14385             };
14386
14387             function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14388
14389               if (newValue == null || oldValueOfValue == null) { // null/undefined
14390                 return newValue === oldValueOfValue;
14391               }
14392
14393               if (typeof newValue === 'object') {
14394
14395                 // attempt to convert the value to a primitive type
14396                 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14397                 //             be cheaply dirty-checked
14398                 newValue = getValueOf(newValue);
14399
14400                 if (typeof newValue === 'object') {
14401                   // objects/arrays are not supported - deep-watching them would be too expensive
14402                   return false;
14403                 }
14404
14405                 // fall-through to the primitive equality check
14406               }
14407
14408               //Primitive or NaN
14409               return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14410             }
14411
14412             function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14413               var inputExpressions = parsedExpression.inputs;
14414               var lastResult;
14415
14416               if (inputExpressions.length === 1) {
14417                 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14418                 inputExpressions = inputExpressions[0];
14419                 return scope.$watch(function expressionInputWatch(scope) {
14420                   var newInputValue = inputExpressions(scope);
14421                   if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14422                     lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14423                     oldInputValueOf = newInputValue && getValueOf(newInputValue);
14424                   }
14425                   return lastResult;
14426                 }, listener, objectEquality, prettyPrintExpression);
14427               }
14428
14429               var oldInputValueOfValues = [];
14430               var oldInputValues = [];
14431               for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14432                 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14433                 oldInputValues[i] = null;
14434               }
14435
14436               return scope.$watch(function expressionInputsWatch(scope) {
14437                 var changed = false;
14438
14439                 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14440                   var newInputValue = inputExpressions[i](scope);
14441                   if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14442                     oldInputValues[i] = newInputValue;
14443                     oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14444                   }
14445                 }
14446
14447                 if (changed) {
14448                   lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14449                 }
14450
14451                 return lastResult;
14452               }, listener, objectEquality, prettyPrintExpression);
14453             }
14454
14455             function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14456               var unwatch, lastValue;
14457               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14458                 return parsedExpression(scope);
14459               }, function oneTimeListener(value, old, scope) {
14460                 lastValue = value;
14461                 if (isFunction(listener)) {
14462                   listener.apply(this, arguments);
14463                 }
14464                 if (isDefined(value)) {
14465                   scope.$$postDigest(function() {
14466                     if (isDefined(lastValue)) {
14467                       unwatch();
14468                     }
14469                   });
14470                 }
14471               }, objectEquality);
14472             }
14473
14474             function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14475               var unwatch, lastValue;
14476               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14477                 return parsedExpression(scope);
14478               }, function oneTimeListener(value, old, scope) {
14479                 lastValue = value;
14480                 if (isFunction(listener)) {
14481                   listener.call(this, value, old, scope);
14482                 }
14483                 if (isAllDefined(value)) {
14484                   scope.$$postDigest(function() {
14485                     if (isAllDefined(lastValue)) unwatch();
14486                   });
14487                 }
14488               }, objectEquality);
14489
14490               function isAllDefined(value) {
14491                 var allDefined = true;
14492                 forEach(value, function(val) {
14493                   if (!isDefined(val)) allDefined = false;
14494                 });
14495                 return allDefined;
14496               }
14497             }
14498
14499             function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14500               var unwatch;
14501               return unwatch = scope.$watch(function constantWatch(scope) {
14502                 return parsedExpression(scope);
14503               }, function constantListener(value, old, scope) {
14504                 if (isFunction(listener)) {
14505                   listener.apply(this, arguments);
14506                 }
14507                 unwatch();
14508               }, objectEquality);
14509             }
14510
14511             function addInterceptor(parsedExpression, interceptorFn) {
14512               if (!interceptorFn) return parsedExpression;
14513               var watchDelegate = parsedExpression.$$watchDelegate;
14514               var useInputs = false;
14515
14516               var regularWatch =
14517                   watchDelegate !== oneTimeLiteralWatchDelegate &&
14518                   watchDelegate !== oneTimeWatchDelegate;
14519
14520               var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14521                 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14522                 return interceptorFn(value, scope, locals);
14523               } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14524                 var value = parsedExpression(scope, locals, assign, inputs);
14525                 var result = interceptorFn(value, scope, locals);
14526                 // we only return the interceptor's result if the
14527                 // initial value is defined (for bind-once)
14528                 return isDefined(value) ? result : value;
14529               };
14530
14531               // Propagate $$watchDelegates other then inputsWatchDelegate
14532               if (parsedExpression.$$watchDelegate &&
14533                   parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14534                 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14535               } else if (!interceptorFn.$stateful) {
14536                 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14537                 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14538                 fn.$$watchDelegate = inputsWatchDelegate;
14539                 useInputs = !parsedExpression.inputs;
14540                 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14541               }
14542
14543               return fn;
14544             }
14545           }];
14546         }
14547
14548         /**
14549          * @ngdoc service
14550          * @name $q
14551          * @requires $rootScope
14552          *
14553          * @description
14554          * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14555          * when they are done processing.
14556          *
14557          * This is an implementation of promises/deferred objects inspired by
14558          * [Kris Kowal's Q](https://github.com/kriskowal/q).
14559          *
14560          * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14561          * implementations, and the other which resembles ES6 promises to some degree.
14562          *
14563          * # $q constructor
14564          *
14565          * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14566          * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14567          * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14568          *
14569          * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14570          * available yet.
14571          *
14572          * It can be used like so:
14573          *
14574          * ```js
14575          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14576          *   // are available in the current lexical scope (they could have been injected or passed in).
14577          *
14578          *   function asyncGreet(name) {
14579          *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14580          *     return $q(function(resolve, reject) {
14581          *       setTimeout(function() {
14582          *         if (okToGreet(name)) {
14583          *           resolve('Hello, ' + name + '!');
14584          *         } else {
14585          *           reject('Greeting ' + name + ' is not allowed.');
14586          *         }
14587          *       }, 1000);
14588          *     });
14589          *   }
14590          *
14591          *   var promise = asyncGreet('Robin Hood');
14592          *   promise.then(function(greeting) {
14593          *     alert('Success: ' + greeting);
14594          *   }, function(reason) {
14595          *     alert('Failed: ' + reason);
14596          *   });
14597          * ```
14598          *
14599          * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14600          *
14601          * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14602          *
14603          * However, the more traditional CommonJS-style usage is still available, and documented below.
14604          *
14605          * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14606          * interface for interacting with an object that represents the result of an action that is
14607          * performed asynchronously, and may or may not be finished at any given point in time.
14608          *
14609          * From the perspective of dealing with error handling, deferred and promise APIs are to
14610          * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14611          *
14612          * ```js
14613          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14614          *   // are available in the current lexical scope (they could have been injected or passed in).
14615          *
14616          *   function asyncGreet(name) {
14617          *     var deferred = $q.defer();
14618          *
14619          *     setTimeout(function() {
14620          *       deferred.notify('About to greet ' + name + '.');
14621          *
14622          *       if (okToGreet(name)) {
14623          *         deferred.resolve('Hello, ' + name + '!');
14624          *       } else {
14625          *         deferred.reject('Greeting ' + name + ' is not allowed.');
14626          *       }
14627          *     }, 1000);
14628          *
14629          *     return deferred.promise;
14630          *   }
14631          *
14632          *   var promise = asyncGreet('Robin Hood');
14633          *   promise.then(function(greeting) {
14634          *     alert('Success: ' + greeting);
14635          *   }, function(reason) {
14636          *     alert('Failed: ' + reason);
14637          *   }, function(update) {
14638          *     alert('Got notification: ' + update);
14639          *   });
14640          * ```
14641          *
14642          * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14643          * comes in the way of guarantees that promise and deferred APIs make, see
14644          * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14645          *
14646          * Additionally the promise api allows for composition that is very hard to do with the
14647          * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14648          * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14649          * section on serial or parallel joining of promises.
14650          *
14651          * # The Deferred API
14652          *
14653          * A new instance of deferred is constructed by calling `$q.defer()`.
14654          *
14655          * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14656          * that can be used for signaling the successful or unsuccessful completion, as well as the status
14657          * of the task.
14658          *
14659          * **Methods**
14660          *
14661          * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14662          *   constructed via `$q.reject`, the promise will be rejected instead.
14663          * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14664          *   resolving it with a rejection constructed via `$q.reject`.
14665          * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14666          *   multiple times before the promise is either resolved or rejected.
14667          *
14668          * **Properties**
14669          *
14670          * - promise – `{Promise}` – promise object associated with this deferred.
14671          *
14672          *
14673          * # The Promise API
14674          *
14675          * A new promise instance is created when a deferred instance is created and can be retrieved by
14676          * calling `deferred.promise`.
14677          *
14678          * The purpose of the promise object is to allow for interested parties to get access to the result
14679          * of the deferred task when it completes.
14680          *
14681          * **Methods**
14682          *
14683          * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14684          *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14685          *   as soon as the result is available. The callbacks are called with a single argument: the result
14686          *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14687          *   provide a progress indication, before the promise is resolved or rejected.
14688          *
14689          *   This method *returns a new promise* which is resolved or rejected via the return value of the
14690          *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14691          *   with the value which is resolved in that promise using
14692          *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14693          *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14694          *   resolved or rejected from the notifyCallback method.
14695          *
14696          * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14697          *
14698          * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14699          *   but to do so without modifying the final value. This is useful to release resources or do some
14700          *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14701          *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14702          *   more information.
14703          *
14704          * # Chaining promises
14705          *
14706          * Because calling the `then` method of a promise returns a new derived promise, it is easily
14707          * possible to create a chain of promises:
14708          *
14709          * ```js
14710          *   promiseB = promiseA.then(function(result) {
14711          *     return result + 1;
14712          *   });
14713          *
14714          *   // promiseB will be resolved immediately after promiseA is resolved and its value
14715          *   // will be the result of promiseA incremented by 1
14716          * ```
14717          *
14718          * It is possible to create chains of any length and since a promise can be resolved with another
14719          * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14720          * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14721          * $http's response interceptors.
14722          *
14723          *
14724          * # Differences between Kris Kowal's Q and $q
14725          *
14726          *  There are two main differences:
14727          *
14728          * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14729          *   mechanism in angular, which means faster propagation of resolution or rejection into your
14730          *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14731          * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14732          *   all the important functionality needed for common async tasks.
14733          *
14734          *  # Testing
14735          *
14736          *  ```js
14737          *    it('should simulate promise', inject(function($q, $rootScope) {
14738          *      var deferred = $q.defer();
14739          *      var promise = deferred.promise;
14740          *      var resolvedValue;
14741          *
14742          *      promise.then(function(value) { resolvedValue = value; });
14743          *      expect(resolvedValue).toBeUndefined();
14744          *
14745          *      // Simulate resolving of promise
14746          *      deferred.resolve(123);
14747          *      // Note that the 'then' function does not get called synchronously.
14748          *      // This is because we want the promise API to always be async, whether or not
14749          *      // it got called synchronously or asynchronously.
14750          *      expect(resolvedValue).toBeUndefined();
14751          *
14752          *      // Propagate promise resolution to 'then' functions using $apply().
14753          *      $rootScope.$apply();
14754          *      expect(resolvedValue).toEqual(123);
14755          *    }));
14756          *  ```
14757          *
14758          * @param {function(function, function)} resolver Function which is responsible for resolving or
14759          *   rejecting the newly created promise. The first parameter is a function which resolves the
14760          *   promise, the second parameter is a function which rejects the promise.
14761          *
14762          * @returns {Promise} The newly created promise.
14763          */
14764         function $QProvider() {
14765
14766           this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14767             return qFactory(function(callback) {
14768               $rootScope.$evalAsync(callback);
14769             }, $exceptionHandler);
14770           }];
14771         }
14772
14773         function $$QProvider() {
14774           this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14775             return qFactory(function(callback) {
14776               $browser.defer(callback);
14777             }, $exceptionHandler);
14778           }];
14779         }
14780
14781         /**
14782          * Constructs a promise manager.
14783          *
14784          * @param {function(function)} nextTick Function for executing functions in the next turn.
14785          * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14786          *     debugging purposes.
14787          * @returns {object} Promise manager.
14788          */
14789         function qFactory(nextTick, exceptionHandler) {
14790           var $qMinErr = minErr('$q', TypeError);
14791           function callOnce(self, resolveFn, rejectFn) {
14792             var called = false;
14793             function wrap(fn) {
14794               return function(value) {
14795                 if (called) return;
14796                 called = true;
14797                 fn.call(self, value);
14798               };
14799             }
14800
14801             return [wrap(resolveFn), wrap(rejectFn)];
14802           }
14803
14804           /**
14805            * @ngdoc method
14806            * @name ng.$q#defer
14807            * @kind function
14808            *
14809            * @description
14810            * Creates a `Deferred` object which represents a task which will finish in the future.
14811            *
14812            * @returns {Deferred} Returns a new instance of deferred.
14813            */
14814           var defer = function() {
14815             return new Deferred();
14816           };
14817
14818           function Promise() {
14819             this.$$state = { status: 0 };
14820           }
14821
14822           extend(Promise.prototype, {
14823             then: function(onFulfilled, onRejected, progressBack) {
14824               if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14825                 return this;
14826               }
14827               var result = new Deferred();
14828
14829               this.$$state.pending = this.$$state.pending || [];
14830               this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14831               if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14832
14833               return result.promise;
14834             },
14835
14836             "catch": function(callback) {
14837               return this.then(null, callback);
14838             },
14839
14840             "finally": function(callback, progressBack) {
14841               return this.then(function(value) {
14842                 return handleCallback(value, true, callback);
14843               }, function(error) {
14844                 return handleCallback(error, false, callback);
14845               }, progressBack);
14846             }
14847           });
14848
14849           //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14850           function simpleBind(context, fn) {
14851             return function(value) {
14852               fn.call(context, value);
14853             };
14854           }
14855
14856           function processQueue(state) {
14857             var fn, deferred, pending;
14858
14859             pending = state.pending;
14860             state.processScheduled = false;
14861             state.pending = undefined;
14862             for (var i = 0, ii = pending.length; i < ii; ++i) {
14863               deferred = pending[i][0];
14864               fn = pending[i][state.status];
14865               try {
14866                 if (isFunction(fn)) {
14867                   deferred.resolve(fn(state.value));
14868                 } else if (state.status === 1) {
14869                   deferred.resolve(state.value);
14870                 } else {
14871                   deferred.reject(state.value);
14872                 }
14873               } catch (e) {
14874                 deferred.reject(e);
14875                 exceptionHandler(e);
14876               }
14877             }
14878           }
14879
14880           function scheduleProcessQueue(state) {
14881             if (state.processScheduled || !state.pending) return;
14882             state.processScheduled = true;
14883             nextTick(function() { processQueue(state); });
14884           }
14885
14886           function Deferred() {
14887             this.promise = new Promise();
14888             //Necessary to support unbound execution :/
14889             this.resolve = simpleBind(this, this.resolve);
14890             this.reject = simpleBind(this, this.reject);
14891             this.notify = simpleBind(this, this.notify);
14892           }
14893
14894           extend(Deferred.prototype, {
14895             resolve: function(val) {
14896               if (this.promise.$$state.status) return;
14897               if (val === this.promise) {
14898                 this.$$reject($qMinErr(
14899                   'qcycle',
14900                   "Expected promise to be resolved with value other than itself '{0}'",
14901                   val));
14902               } else {
14903                 this.$$resolve(val);
14904               }
14905
14906             },
14907
14908             $$resolve: function(val) {
14909               var then, fns;
14910
14911               fns = callOnce(this, this.$$resolve, this.$$reject);
14912               try {
14913                 if ((isObject(val) || isFunction(val))) then = val && val.then;
14914                 if (isFunction(then)) {
14915                   this.promise.$$state.status = -1;
14916                   then.call(val, fns[0], fns[1], this.notify);
14917                 } else {
14918                   this.promise.$$state.value = val;
14919                   this.promise.$$state.status = 1;
14920                   scheduleProcessQueue(this.promise.$$state);
14921                 }
14922               } catch (e) {
14923                 fns[1](e);
14924                 exceptionHandler(e);
14925               }
14926             },
14927
14928             reject: function(reason) {
14929               if (this.promise.$$state.status) return;
14930               this.$$reject(reason);
14931             },
14932
14933             $$reject: function(reason) {
14934               this.promise.$$state.value = reason;
14935               this.promise.$$state.status = 2;
14936               scheduleProcessQueue(this.promise.$$state);
14937             },
14938
14939             notify: function(progress) {
14940               var callbacks = this.promise.$$state.pending;
14941
14942               if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14943                 nextTick(function() {
14944                   var callback, result;
14945                   for (var i = 0, ii = callbacks.length; i < ii; i++) {
14946                     result = callbacks[i][0];
14947                     callback = callbacks[i][3];
14948                     try {
14949                       result.notify(isFunction(callback) ? callback(progress) : progress);
14950                     } catch (e) {
14951                       exceptionHandler(e);
14952                     }
14953                   }
14954                 });
14955               }
14956             }
14957           });
14958
14959           /**
14960            * @ngdoc method
14961            * @name $q#reject
14962            * @kind function
14963            *
14964            * @description
14965            * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14966            * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14967            * a promise chain, you don't need to worry about it.
14968            *
14969            * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14970            * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14971            * a promise error callback and you want to forward the error to the promise derived from the
14972            * current promise, you have to "rethrow" the error by returning a rejection constructed via
14973            * `reject`.
14974            *
14975            * ```js
14976            *   promiseB = promiseA.then(function(result) {
14977            *     // success: do something and resolve promiseB
14978            *     //          with the old or a new result
14979            *     return result;
14980            *   }, function(reason) {
14981            *     // error: handle the error if possible and
14982            *     //        resolve promiseB with newPromiseOrValue,
14983            *     //        otherwise forward the rejection to promiseB
14984            *     if (canHandle(reason)) {
14985            *      // handle the error and recover
14986            *      return newPromiseOrValue;
14987            *     }
14988            *     return $q.reject(reason);
14989            *   });
14990            * ```
14991            *
14992            * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14993            * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14994            */
14995           var reject = function(reason) {
14996             var result = new Deferred();
14997             result.reject(reason);
14998             return result.promise;
14999           };
15000
15001           var makePromise = function makePromise(value, resolved) {
15002             var result = new Deferred();
15003             if (resolved) {
15004               result.resolve(value);
15005             } else {
15006               result.reject(value);
15007             }
15008             return result.promise;
15009           };
15010
15011           var handleCallback = function handleCallback(value, isResolved, callback) {
15012             var callbackOutput = null;
15013             try {
15014               if (isFunction(callback)) callbackOutput = callback();
15015             } catch (e) {
15016               return makePromise(e, false);
15017             }
15018             if (isPromiseLike(callbackOutput)) {
15019               return callbackOutput.then(function() {
15020                 return makePromise(value, isResolved);
15021               }, function(error) {
15022                 return makePromise(error, false);
15023               });
15024             } else {
15025               return makePromise(value, isResolved);
15026             }
15027           };
15028
15029           /**
15030            * @ngdoc method
15031            * @name $q#when
15032            * @kind function
15033            *
15034            * @description
15035            * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15036            * This is useful when you are dealing with an object that might or might not be a promise, or if
15037            * the promise comes from a source that can't be trusted.
15038            *
15039            * @param {*} value Value or a promise
15040            * @param {Function=} successCallback
15041            * @param {Function=} errorCallback
15042            * @param {Function=} progressCallback
15043            * @returns {Promise} Returns a promise of the passed value or promise
15044            */
15045
15046
15047           var when = function(value, callback, errback, progressBack) {
15048             var result = new Deferred();
15049             result.resolve(value);
15050             return result.promise.then(callback, errback, progressBack);
15051           };
15052
15053           /**
15054            * @ngdoc method
15055            * @name $q#resolve
15056            * @kind function
15057            *
15058            * @description
15059            * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15060            *
15061            * @param {*} value Value or a promise
15062            * @param {Function=} successCallback
15063            * @param {Function=} errorCallback
15064            * @param {Function=} progressCallback
15065            * @returns {Promise} Returns a promise of the passed value or promise
15066            */
15067           var resolve = when;
15068
15069           /**
15070            * @ngdoc method
15071            * @name $q#all
15072            * @kind function
15073            *
15074            * @description
15075            * Combines multiple promises into a single promise that is resolved when all of the input
15076            * promises are resolved.
15077            *
15078            * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15079            * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15080            *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15081            *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15082            *   with the same rejection value.
15083            */
15084
15085           function all(promises) {
15086             var deferred = new Deferred(),
15087                 counter = 0,
15088                 results = isArray(promises) ? [] : {};
15089
15090             forEach(promises, function(promise, key) {
15091               counter++;
15092               when(promise).then(function(value) {
15093                 if (results.hasOwnProperty(key)) return;
15094                 results[key] = value;
15095                 if (!(--counter)) deferred.resolve(results);
15096               }, function(reason) {
15097                 if (results.hasOwnProperty(key)) return;
15098                 deferred.reject(reason);
15099               });
15100             });
15101
15102             if (counter === 0) {
15103               deferred.resolve(results);
15104             }
15105
15106             return deferred.promise;
15107           }
15108
15109           var $Q = function Q(resolver) {
15110             if (!isFunction(resolver)) {
15111               throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15112             }
15113
15114             if (!(this instanceof Q)) {
15115               // More useful when $Q is the Promise itself.
15116               return new Q(resolver);
15117             }
15118
15119             var deferred = new Deferred();
15120
15121             function resolveFn(value) {
15122               deferred.resolve(value);
15123             }
15124
15125             function rejectFn(reason) {
15126               deferred.reject(reason);
15127             }
15128
15129             resolver(resolveFn, rejectFn);
15130
15131             return deferred.promise;
15132           };
15133
15134           $Q.defer = defer;
15135           $Q.reject = reject;
15136           $Q.when = when;
15137           $Q.resolve = resolve;
15138           $Q.all = all;
15139
15140           return $Q;
15141         }
15142
15143         function $$RAFProvider() { //rAF
15144           this.$get = ['$window', '$timeout', function($window, $timeout) {
15145             var requestAnimationFrame = $window.requestAnimationFrame ||
15146                                         $window.webkitRequestAnimationFrame;
15147
15148             var cancelAnimationFrame = $window.cancelAnimationFrame ||
15149                                        $window.webkitCancelAnimationFrame ||
15150                                        $window.webkitCancelRequestAnimationFrame;
15151
15152             var rafSupported = !!requestAnimationFrame;
15153             var raf = rafSupported
15154               ? function(fn) {
15155                   var id = requestAnimationFrame(fn);
15156                   return function() {
15157                     cancelAnimationFrame(id);
15158                   };
15159                 }
15160               : function(fn) {
15161                   var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15162                   return function() {
15163                     $timeout.cancel(timer);
15164                   };
15165                 };
15166
15167             raf.supported = rafSupported;
15168
15169             return raf;
15170           }];
15171         }
15172
15173         /**
15174          * DESIGN NOTES
15175          *
15176          * The design decisions behind the scope are heavily favored for speed and memory consumption.
15177          *
15178          * The typical use of scope is to watch the expressions, which most of the time return the same
15179          * value as last time so we optimize the operation.
15180          *
15181          * Closures construction is expensive in terms of speed as well as memory:
15182          *   - No closures, instead use prototypical inheritance for API
15183          *   - Internal state needs to be stored on scope directly, which means that private state is
15184          *     exposed as $$____ properties
15185          *
15186          * Loop operations are optimized by using while(count--) { ... }
15187          *   - This means that in order to keep the same order of execution as addition we have to add
15188          *     items to the array at the beginning (unshift) instead of at the end (push)
15189          *
15190          * Child scopes are created and removed often
15191          *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15192          *
15193          * There are fewer watches than observers. This is why you don't want the observer to be implemented
15194          * in the same way as watch. Watch requires return of the initialization function which is expensive
15195          * to construct.
15196          */
15197
15198
15199         /**
15200          * @ngdoc provider
15201          * @name $rootScopeProvider
15202          * @description
15203          *
15204          * Provider for the $rootScope service.
15205          */
15206
15207         /**
15208          * @ngdoc method
15209          * @name $rootScopeProvider#digestTtl
15210          * @description
15211          *
15212          * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15213          * assuming that the model is unstable.
15214          *
15215          * The current default is 10 iterations.
15216          *
15217          * In complex applications it's possible that the dependencies between `$watch`s will result in
15218          * several digest iterations. However if an application needs more than the default 10 digest
15219          * iterations for its model to stabilize then you should investigate what is causing the model to
15220          * continuously change during the digest.
15221          *
15222          * Increasing the TTL could have performance implications, so you should not change it without
15223          * proper justification.
15224          *
15225          * @param {number} limit The number of digest iterations.
15226          */
15227
15228
15229         /**
15230          * @ngdoc service
15231          * @name $rootScope
15232          * @description
15233          *
15234          * Every application has a single root {@link ng.$rootScope.Scope scope}.
15235          * All other scopes are descendant scopes of the root scope. Scopes provide separation
15236          * between the model and the view, via a mechanism for watching the model for changes.
15237          * They also provide event emission/broadcast and subscription facility. See the
15238          * {@link guide/scope developer guide on scopes}.
15239          */
15240         function $RootScopeProvider() {
15241           var TTL = 10;
15242           var $rootScopeMinErr = minErr('$rootScope');
15243           var lastDirtyWatch = null;
15244           var applyAsyncId = null;
15245
15246           this.digestTtl = function(value) {
15247             if (arguments.length) {
15248               TTL = value;
15249             }
15250             return TTL;
15251           };
15252
15253           function createChildScopeClass(parent) {
15254             function ChildScope() {
15255               this.$$watchers = this.$$nextSibling =
15256                   this.$$childHead = this.$$childTail = null;
15257               this.$$listeners = {};
15258               this.$$listenerCount = {};
15259               this.$$watchersCount = 0;
15260               this.$id = nextUid();
15261               this.$$ChildScope = null;
15262             }
15263             ChildScope.prototype = parent;
15264             return ChildScope;
15265           }
15266
15267           this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15268               function($injector, $exceptionHandler, $parse, $browser) {
15269
15270             function destroyChildScope($event) {
15271                 $event.currentScope.$$destroyed = true;
15272             }
15273
15274             function cleanUpScope($scope) {
15275
15276               if (msie === 9) {
15277                 // There is a memory leak in IE9 if all child scopes are not disconnected
15278                 // completely when a scope is destroyed. So this code will recurse up through
15279                 // all this scopes children
15280                 //
15281                 // See issue https://github.com/angular/angular.js/issues/10706
15282                 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15283                 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15284               }
15285
15286               // The code below works around IE9 and V8's memory leaks
15287               //
15288               // See:
15289               // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15290               // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15291               // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15292
15293               $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15294                   $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15295             }
15296
15297             /**
15298              * @ngdoc type
15299              * @name $rootScope.Scope
15300              *
15301              * @description
15302              * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15303              * {@link auto.$injector $injector}. Child scopes are created using the
15304              * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15305              * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15306              * an in-depth introduction and usage examples.
15307              *
15308              *
15309              * # Inheritance
15310              * A scope can inherit from a parent scope, as in this example:
15311              * ```js
15312                  var parent = $rootScope;
15313                  var child = parent.$new();
15314
15315                  parent.salutation = "Hello";
15316                  expect(child.salutation).toEqual('Hello');
15317
15318                  child.salutation = "Welcome";
15319                  expect(child.salutation).toEqual('Welcome');
15320                  expect(parent.salutation).toEqual('Hello');
15321              * ```
15322              *
15323              * When interacting with `Scope` in tests, additional helper methods are available on the
15324              * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15325              * details.
15326              *
15327              *
15328              * @param {Object.<string, function()>=} providers Map of service factory which need to be
15329              *                                       provided for the current scope. Defaults to {@link ng}.
15330              * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15331              *                              append/override services provided by `providers`. This is handy
15332              *                              when unit-testing and having the need to override a default
15333              *                              service.
15334              * @returns {Object} Newly created scope.
15335              *
15336              */
15337             function Scope() {
15338               this.$id = nextUid();
15339               this.$$phase = this.$parent = this.$$watchers =
15340                              this.$$nextSibling = this.$$prevSibling =
15341                              this.$$childHead = this.$$childTail = null;
15342               this.$root = this;
15343               this.$$destroyed = false;
15344               this.$$listeners = {};
15345               this.$$listenerCount = {};
15346               this.$$watchersCount = 0;
15347               this.$$isolateBindings = null;
15348             }
15349
15350             /**
15351              * @ngdoc property
15352              * @name $rootScope.Scope#$id
15353              *
15354              * @description
15355              * Unique scope ID (monotonically increasing) useful for debugging.
15356              */
15357
15358              /**
15359               * @ngdoc property
15360               * @name $rootScope.Scope#$parent
15361               *
15362               * @description
15363               * Reference to the parent scope.
15364               */
15365
15366               /**
15367                * @ngdoc property
15368                * @name $rootScope.Scope#$root
15369                *
15370                * @description
15371                * Reference to the root scope.
15372                */
15373
15374             Scope.prototype = {
15375               constructor: Scope,
15376               /**
15377                * @ngdoc method
15378                * @name $rootScope.Scope#$new
15379                * @kind function
15380                *
15381                * @description
15382                * Creates a new child {@link ng.$rootScope.Scope scope}.
15383                *
15384                * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15385                * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15386                *
15387                * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15388                * desired for the scope and its child scopes to be permanently detached from the parent and
15389                * thus stop participating in model change detection and listener notification by invoking.
15390                *
15391                * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15392                *         parent scope. The scope is isolated, as it can not see parent scope properties.
15393                *         When creating widgets, it is useful for the widget to not accidentally read parent
15394                *         state.
15395                *
15396                * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15397                *                              of the newly created scope. Defaults to `this` scope if not provided.
15398                *                              This is used when creating a transclude scope to correctly place it
15399                *                              in the scope hierarchy while maintaining the correct prototypical
15400                *                              inheritance.
15401                *
15402                * @returns {Object} The newly created child scope.
15403                *
15404                */
15405               $new: function(isolate, parent) {
15406                 var child;
15407
15408                 parent = parent || this;
15409
15410                 if (isolate) {
15411                   child = new Scope();
15412                   child.$root = this.$root;
15413                 } else {
15414                   // Only create a child scope class if somebody asks for one,
15415                   // but cache it to allow the VM to optimize lookups.
15416                   if (!this.$$ChildScope) {
15417                     this.$$ChildScope = createChildScopeClass(this);
15418                   }
15419                   child = new this.$$ChildScope();
15420                 }
15421                 child.$parent = parent;
15422                 child.$$prevSibling = parent.$$childTail;
15423                 if (parent.$$childHead) {
15424                   parent.$$childTail.$$nextSibling = child;
15425                   parent.$$childTail = child;
15426                 } else {
15427                   parent.$$childHead = parent.$$childTail = child;
15428                 }
15429
15430                 // When the new scope is not isolated or we inherit from `this`, and
15431                 // the parent scope is destroyed, the property `$$destroyed` is inherited
15432                 // prototypically. In all other cases, this property needs to be set
15433                 // when the parent scope is destroyed.
15434                 // The listener needs to be added after the parent is set
15435                 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15436
15437                 return child;
15438               },
15439
15440               /**
15441                * @ngdoc method
15442                * @name $rootScope.Scope#$watch
15443                * @kind function
15444                *
15445                * @description
15446                * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15447                *
15448                * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15449                *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15450                *   its value when executed multiple times with the same input because it may be executed multiple
15451                *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15452                *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15453                * - The `listener` is called only when the value from the current `watchExpression` and the
15454                *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15455                *   see below). Inequality is determined according to reference inequality,
15456                *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15457                *    via the `!==` Javascript operator, unless `objectEquality == true`
15458                *   (see next point)
15459                * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15460                *   according to the {@link angular.equals} function. To save the value of the object for
15461                *   later comparison, the {@link angular.copy} function is used. This therefore means that
15462                *   watching complex objects will have adverse memory and performance implications.
15463                * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15464                *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15465                *   iteration limit is 10 to prevent an infinite loop deadlock.
15466                *
15467                *
15468                * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15469                * you can register a `watchExpression` function with no `listener`. (Be prepared for
15470                * multiple calls to your `watchExpression` because it will execute multiple times in a
15471                * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15472                *
15473                * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15474                * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15475                * watcher. In rare cases, this is undesirable because the listener is called when the result
15476                * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15477                * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15478                * listener was called due to initialization.
15479                *
15480                *
15481                *
15482                * # Example
15483                * ```js
15484                    // let's assume that scope was dependency injected as the $rootScope
15485                    var scope = $rootScope;
15486                    scope.name = 'misko';
15487                    scope.counter = 0;
15488
15489                    expect(scope.counter).toEqual(0);
15490                    scope.$watch('name', function(newValue, oldValue) {
15491                      scope.counter = scope.counter + 1;
15492                    });
15493                    expect(scope.counter).toEqual(0);
15494
15495                    scope.$digest();
15496                    // the listener is always called during the first $digest loop after it was registered
15497                    expect(scope.counter).toEqual(1);
15498
15499                    scope.$digest();
15500                    // but now it will not be called unless the value changes
15501                    expect(scope.counter).toEqual(1);
15502
15503                    scope.name = 'adam';
15504                    scope.$digest();
15505                    expect(scope.counter).toEqual(2);
15506
15507
15508
15509                    // Using a function as a watchExpression
15510                    var food;
15511                    scope.foodCounter = 0;
15512                    expect(scope.foodCounter).toEqual(0);
15513                    scope.$watch(
15514                      // This function returns the value being watched. It is called for each turn of the $digest loop
15515                      function() { return food; },
15516                      // This is the change listener, called when the value returned from the above function changes
15517                      function(newValue, oldValue) {
15518                        if ( newValue !== oldValue ) {
15519                          // Only increment the counter if the value changed
15520                          scope.foodCounter = scope.foodCounter + 1;
15521                        }
15522                      }
15523                    );
15524                    // No digest has been run so the counter will be zero
15525                    expect(scope.foodCounter).toEqual(0);
15526
15527                    // Run the digest but since food has not changed count will still be zero
15528                    scope.$digest();
15529                    expect(scope.foodCounter).toEqual(0);
15530
15531                    // Update food and run digest.  Now the counter will increment
15532                    food = 'cheeseburger';
15533                    scope.$digest();
15534                    expect(scope.foodCounter).toEqual(1);
15535
15536                * ```
15537                *
15538                *
15539                *
15540                * @param {(function()|string)} watchExpression Expression that is evaluated on each
15541                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15542                *    a call to the `listener`.
15543                *
15544                *    - `string`: Evaluated as {@link guide/expression expression}
15545                *    - `function(scope)`: called with current `scope` as a parameter.
15546                * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15547                *    of `watchExpression` changes.
15548                *
15549                *    - `newVal` contains the current value of the `watchExpression`
15550                *    - `oldVal` contains the previous value of the `watchExpression`
15551                *    - `scope` refers to the current scope
15552                * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15553                *     comparing for reference equality.
15554                * @returns {function()} Returns a deregistration function for this listener.
15555                */
15556               $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15557                 var get = $parse(watchExp);
15558
15559                 if (get.$$watchDelegate) {
15560                   return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15561                 }
15562                 var scope = this,
15563                     array = scope.$$watchers,
15564                     watcher = {
15565                       fn: listener,
15566                       last: initWatchVal,
15567                       get: get,
15568                       exp: prettyPrintExpression || watchExp,
15569                       eq: !!objectEquality
15570                     };
15571
15572                 lastDirtyWatch = null;
15573
15574                 if (!isFunction(listener)) {
15575                   watcher.fn = noop;
15576                 }
15577
15578                 if (!array) {
15579                   array = scope.$$watchers = [];
15580                 }
15581                 // we use unshift since we use a while loop in $digest for speed.
15582                 // the while loop reads in reverse order.
15583                 array.unshift(watcher);
15584                 incrementWatchersCount(this, 1);
15585
15586                 return function deregisterWatch() {
15587                   if (arrayRemove(array, watcher) >= 0) {
15588                     incrementWatchersCount(scope, -1);
15589                   }
15590                   lastDirtyWatch = null;
15591                 };
15592               },
15593
15594               /**
15595                * @ngdoc method
15596                * @name $rootScope.Scope#$watchGroup
15597                * @kind function
15598                *
15599                * @description
15600                * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15601                * If any one expression in the collection changes the `listener` is executed.
15602                *
15603                * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15604                *   call to $digest() to see if any items changes.
15605                * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15606                *
15607                * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15608                * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15609                *
15610                * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15611                *    expression in `watchExpressions` changes
15612                *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15613                *    those of `watchExpression`
15614                *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15615                *    those of `watchExpression`
15616                *    The `scope` refers to the current scope.
15617                * @returns {function()} Returns a de-registration function for all listeners.
15618                */
15619               $watchGroup: function(watchExpressions, listener) {
15620                 var oldValues = new Array(watchExpressions.length);
15621                 var newValues = new Array(watchExpressions.length);
15622                 var deregisterFns = [];
15623                 var self = this;
15624                 var changeReactionScheduled = false;
15625                 var firstRun = true;
15626
15627                 if (!watchExpressions.length) {
15628                   // No expressions means we call the listener ASAP
15629                   var shouldCall = true;
15630                   self.$evalAsync(function() {
15631                     if (shouldCall) listener(newValues, newValues, self);
15632                   });
15633                   return function deregisterWatchGroup() {
15634                     shouldCall = false;
15635                   };
15636                 }
15637
15638                 if (watchExpressions.length === 1) {
15639                   // Special case size of one
15640                   return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15641                     newValues[0] = value;
15642                     oldValues[0] = oldValue;
15643                     listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15644                   });
15645                 }
15646
15647                 forEach(watchExpressions, function(expr, i) {
15648                   var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15649                     newValues[i] = value;
15650                     oldValues[i] = oldValue;
15651                     if (!changeReactionScheduled) {
15652                       changeReactionScheduled = true;
15653                       self.$evalAsync(watchGroupAction);
15654                     }
15655                   });
15656                   deregisterFns.push(unwatchFn);
15657                 });
15658
15659                 function watchGroupAction() {
15660                   changeReactionScheduled = false;
15661
15662                   if (firstRun) {
15663                     firstRun = false;
15664                     listener(newValues, newValues, self);
15665                   } else {
15666                     listener(newValues, oldValues, self);
15667                   }
15668                 }
15669
15670                 return function deregisterWatchGroup() {
15671                   while (deregisterFns.length) {
15672                     deregisterFns.shift()();
15673                   }
15674                 };
15675               },
15676
15677
15678               /**
15679                * @ngdoc method
15680                * @name $rootScope.Scope#$watchCollection
15681                * @kind function
15682                *
15683                * @description
15684                * Shallow watches the properties of an object and fires whenever any of the properties change
15685                * (for arrays, this implies watching the array items; for object maps, this implies watching
15686                * the properties). If a change is detected, the `listener` callback is fired.
15687                *
15688                * - The `obj` collection is observed via standard $watch operation and is examined on every
15689                *   call to $digest() to see if any items have been added, removed, or moved.
15690                * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15691                *   adding, removing, and moving items belonging to an object or array.
15692                *
15693                *
15694                * # Example
15695                * ```js
15696                   $scope.names = ['igor', 'matias', 'misko', 'james'];
15697                   $scope.dataCount = 4;
15698
15699                   $scope.$watchCollection('names', function(newNames, oldNames) {
15700                     $scope.dataCount = newNames.length;
15701                   });
15702
15703                   expect($scope.dataCount).toEqual(4);
15704                   $scope.$digest();
15705
15706                   //still at 4 ... no changes
15707                   expect($scope.dataCount).toEqual(4);
15708
15709                   $scope.names.pop();
15710                   $scope.$digest();
15711
15712                   //now there's been a change
15713                   expect($scope.dataCount).toEqual(3);
15714                * ```
15715                *
15716                *
15717                * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15718                *    expression value should evaluate to an object or an array which is observed on each
15719                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15720                *    collection will trigger a call to the `listener`.
15721                *
15722                * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15723                *    when a change is detected.
15724                *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15725                *    - The `oldCollection` object is a copy of the former collection data.
15726                *      Due to performance considerations, the`oldCollection` value is computed only if the
15727                *      `listener` function declares two or more arguments.
15728                *    - The `scope` argument refers to the current scope.
15729                *
15730                * @returns {function()} Returns a de-registration function for this listener. When the
15731                *    de-registration function is executed, the internal watch operation is terminated.
15732                */
15733               $watchCollection: function(obj, listener) {
15734                 $watchCollectionInterceptor.$stateful = true;
15735
15736                 var self = this;
15737                 // the current value, updated on each dirty-check run
15738                 var newValue;
15739                 // a shallow copy of the newValue from the last dirty-check run,
15740                 // updated to match newValue during dirty-check run
15741                 var oldValue;
15742                 // a shallow copy of the newValue from when the last change happened
15743                 var veryOldValue;
15744                 // only track veryOldValue if the listener is asking for it
15745                 var trackVeryOldValue = (listener.length > 1);
15746                 var changeDetected = 0;
15747                 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15748                 var internalArray = [];
15749                 var internalObject = {};
15750                 var initRun = true;
15751                 var oldLength = 0;
15752
15753                 function $watchCollectionInterceptor(_value) {
15754                   newValue = _value;
15755                   var newLength, key, bothNaN, newItem, oldItem;
15756
15757                   // If the new value is undefined, then return undefined as the watch may be a one-time watch
15758                   if (isUndefined(newValue)) return;
15759
15760                   if (!isObject(newValue)) { // if primitive
15761                     if (oldValue !== newValue) {
15762                       oldValue = newValue;
15763                       changeDetected++;
15764                     }
15765                   } else if (isArrayLike(newValue)) {
15766                     if (oldValue !== internalArray) {
15767                       // we are transitioning from something which was not an array into array.
15768                       oldValue = internalArray;
15769                       oldLength = oldValue.length = 0;
15770                       changeDetected++;
15771                     }
15772
15773                     newLength = newValue.length;
15774
15775                     if (oldLength !== newLength) {
15776                       // if lengths do not match we need to trigger change notification
15777                       changeDetected++;
15778                       oldValue.length = oldLength = newLength;
15779                     }
15780                     // copy the items to oldValue and look for changes.
15781                     for (var i = 0; i < newLength; i++) {
15782                       oldItem = oldValue[i];
15783                       newItem = newValue[i];
15784
15785                       bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15786                       if (!bothNaN && (oldItem !== newItem)) {
15787                         changeDetected++;
15788                         oldValue[i] = newItem;
15789                       }
15790                     }
15791                   } else {
15792                     if (oldValue !== internalObject) {
15793                       // we are transitioning from something which was not an object into object.
15794                       oldValue = internalObject = {};
15795                       oldLength = 0;
15796                       changeDetected++;
15797                     }
15798                     // copy the items to oldValue and look for changes.
15799                     newLength = 0;
15800                     for (key in newValue) {
15801                       if (hasOwnProperty.call(newValue, key)) {
15802                         newLength++;
15803                         newItem = newValue[key];
15804                         oldItem = oldValue[key];
15805
15806                         if (key in oldValue) {
15807                           bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15808                           if (!bothNaN && (oldItem !== newItem)) {
15809                             changeDetected++;
15810                             oldValue[key] = newItem;
15811                           }
15812                         } else {
15813                           oldLength++;
15814                           oldValue[key] = newItem;
15815                           changeDetected++;
15816                         }
15817                       }
15818                     }
15819                     if (oldLength > newLength) {
15820                       // we used to have more keys, need to find them and destroy them.
15821                       changeDetected++;
15822                       for (key in oldValue) {
15823                         if (!hasOwnProperty.call(newValue, key)) {
15824                           oldLength--;
15825                           delete oldValue[key];
15826                         }
15827                       }
15828                     }
15829                   }
15830                   return changeDetected;
15831                 }
15832
15833                 function $watchCollectionAction() {
15834                   if (initRun) {
15835                     initRun = false;
15836                     listener(newValue, newValue, self);
15837                   } else {
15838                     listener(newValue, veryOldValue, self);
15839                   }
15840
15841                   // make a copy for the next time a collection is changed
15842                   if (trackVeryOldValue) {
15843                     if (!isObject(newValue)) {
15844                       //primitive
15845                       veryOldValue = newValue;
15846                     } else if (isArrayLike(newValue)) {
15847                       veryOldValue = new Array(newValue.length);
15848                       for (var i = 0; i < newValue.length; i++) {
15849                         veryOldValue[i] = newValue[i];
15850                       }
15851                     } else { // if object
15852                       veryOldValue = {};
15853                       for (var key in newValue) {
15854                         if (hasOwnProperty.call(newValue, key)) {
15855                           veryOldValue[key] = newValue[key];
15856                         }
15857                       }
15858                     }
15859                   }
15860                 }
15861
15862                 return this.$watch(changeDetector, $watchCollectionAction);
15863               },
15864
15865               /**
15866                * @ngdoc method
15867                * @name $rootScope.Scope#$digest
15868                * @kind function
15869                *
15870                * @description
15871                * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15872                * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15873                * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15874                * until no more listeners are firing. This means that it is possible to get into an infinite
15875                * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15876                * iterations exceeds 10.
15877                *
15878                * Usually, you don't call `$digest()` directly in
15879                * {@link ng.directive:ngController controllers} or in
15880                * {@link ng.$compileProvider#directive directives}.
15881                * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15882                * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15883                *
15884                * If you want to be notified whenever `$digest()` is called,
15885                * you can register a `watchExpression` function with
15886                * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15887                *
15888                * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15889                *
15890                * # Example
15891                * ```js
15892                    var scope = ...;
15893                    scope.name = 'misko';
15894                    scope.counter = 0;
15895
15896                    expect(scope.counter).toEqual(0);
15897                    scope.$watch('name', function(newValue, oldValue) {
15898                      scope.counter = scope.counter + 1;
15899                    });
15900                    expect(scope.counter).toEqual(0);
15901
15902                    scope.$digest();
15903                    // the listener is always called during the first $digest loop after it was registered
15904                    expect(scope.counter).toEqual(1);
15905
15906                    scope.$digest();
15907                    // but now it will not be called unless the value changes
15908                    expect(scope.counter).toEqual(1);
15909
15910                    scope.name = 'adam';
15911                    scope.$digest();
15912                    expect(scope.counter).toEqual(2);
15913                * ```
15914                *
15915                */
15916               $digest: function() {
15917                 var watch, value, last,
15918                     watchers,
15919                     length,
15920                     dirty, ttl = TTL,
15921                     next, current, target = this,
15922                     watchLog = [],
15923                     logIdx, logMsg, asyncTask;
15924
15925                 beginPhase('$digest');
15926                 // Check for changes to browser url that happened in sync before the call to $digest
15927                 $browser.$$checkUrlChange();
15928
15929                 if (this === $rootScope && applyAsyncId !== null) {
15930                   // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15931                   // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15932                   $browser.defer.cancel(applyAsyncId);
15933                   flushApplyAsync();
15934                 }
15935
15936                 lastDirtyWatch = null;
15937
15938                 do { // "while dirty" loop
15939                   dirty = false;
15940                   current = target;
15941
15942                   while (asyncQueue.length) {
15943                     try {
15944                       asyncTask = asyncQueue.shift();
15945                       asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15946                     } catch (e) {
15947                       $exceptionHandler(e);
15948                     }
15949                     lastDirtyWatch = null;
15950                   }
15951
15952                   traverseScopesLoop:
15953                   do { // "traverse the scopes" loop
15954                     if ((watchers = current.$$watchers)) {
15955                       // process our watches
15956                       length = watchers.length;
15957                       while (length--) {
15958                         try {
15959                           watch = watchers[length];
15960                           // Most common watches are on primitives, in which case we can short
15961                           // circuit it with === operator, only when === fails do we use .equals
15962                           if (watch) {
15963                             if ((value = watch.get(current)) !== (last = watch.last) &&
15964                                 !(watch.eq
15965                                     ? equals(value, last)
15966                                     : (typeof value === 'number' && typeof last === 'number'
15967                                        && isNaN(value) && isNaN(last)))) {
15968                               dirty = true;
15969                               lastDirtyWatch = watch;
15970                               watch.last = watch.eq ? copy(value, null) : value;
15971                               watch.fn(value, ((last === initWatchVal) ? value : last), current);
15972                               if (ttl < 5) {
15973                                 logIdx = 4 - ttl;
15974                                 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15975                                 watchLog[logIdx].push({
15976                                   msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15977                                   newVal: value,
15978                                   oldVal: last
15979                                 });
15980                               }
15981                             } else if (watch === lastDirtyWatch) {
15982                               // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15983                               // have already been tested.
15984                               dirty = false;
15985                               break traverseScopesLoop;
15986                             }
15987                           }
15988                         } catch (e) {
15989                           $exceptionHandler(e);
15990                         }
15991                       }
15992                     }
15993
15994                     // Insanity Warning: scope depth-first traversal
15995                     // yes, this code is a bit crazy, but it works and we have tests to prove it!
15996                     // this piece should be kept in sync with the traversal in $broadcast
15997                     if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15998                         (current !== target && current.$$nextSibling)))) {
15999                       while (current !== target && !(next = current.$$nextSibling)) {
16000                         current = current.$parent;
16001                       }
16002                     }
16003                   } while ((current = next));
16004
16005                   // `break traverseScopesLoop;` takes us to here
16006
16007                   if ((dirty || asyncQueue.length) && !(ttl--)) {
16008                     clearPhase();
16009                     throw $rootScopeMinErr('infdig',
16010                         '{0} $digest() iterations reached. Aborting!\n' +
16011                         'Watchers fired in the last 5 iterations: {1}',
16012                         TTL, watchLog);
16013                   }
16014
16015                 } while (dirty || asyncQueue.length);
16016
16017                 clearPhase();
16018
16019                 while (postDigestQueue.length) {
16020                   try {
16021                     postDigestQueue.shift()();
16022                   } catch (e) {
16023                     $exceptionHandler(e);
16024                   }
16025                 }
16026               },
16027
16028
16029               /**
16030                * @ngdoc event
16031                * @name $rootScope.Scope#$destroy
16032                * @eventType broadcast on scope being destroyed
16033                *
16034                * @description
16035                * Broadcasted when a scope and its children are being destroyed.
16036                *
16037                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16038                * clean up DOM bindings before an element is removed from the DOM.
16039                */
16040
16041               /**
16042                * @ngdoc method
16043                * @name $rootScope.Scope#$destroy
16044                * @kind function
16045                *
16046                * @description
16047                * Removes the current scope (and all of its children) from the parent scope. Removal implies
16048                * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16049                * propagate to the current scope and its children. Removal also implies that the current
16050                * scope is eligible for garbage collection.
16051                *
16052                * The `$destroy()` is usually used by directives such as
16053                * {@link ng.directive:ngRepeat ngRepeat} for managing the
16054                * unrolling of the loop.
16055                *
16056                * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16057                * Application code can register a `$destroy` event handler that will give it a chance to
16058                * perform any necessary cleanup.
16059                *
16060                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16061                * clean up DOM bindings before an element is removed from the DOM.
16062                */
16063               $destroy: function() {
16064                 // We can't destroy a scope that has been already destroyed.
16065                 if (this.$$destroyed) return;
16066                 var parent = this.$parent;
16067
16068                 this.$broadcast('$destroy');
16069                 this.$$destroyed = true;
16070
16071                 if (this === $rootScope) {
16072                   //Remove handlers attached to window when $rootScope is removed
16073                   $browser.$$applicationDestroyed();
16074                 }
16075
16076                 incrementWatchersCount(this, -this.$$watchersCount);
16077                 for (var eventName in this.$$listenerCount) {
16078                   decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16079                 }
16080
16081                 // sever all the references to parent scopes (after this cleanup, the current scope should
16082                 // not be retained by any of our references and should be eligible for garbage collection)
16083                 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16084                 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16085                 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16086                 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16087
16088                 // Disable listeners, watchers and apply/digest methods
16089                 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16090                 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16091                 this.$$listeners = {};
16092
16093                 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16094                 this.$$nextSibling = null;
16095                 cleanUpScope(this);
16096               },
16097
16098               /**
16099                * @ngdoc method
16100                * @name $rootScope.Scope#$eval
16101                * @kind function
16102                *
16103                * @description
16104                * Executes the `expression` on the current scope and returns the result. Any exceptions in
16105                * the expression are propagated (uncaught). This is useful when evaluating Angular
16106                * expressions.
16107                *
16108                * # Example
16109                * ```js
16110                    var scope = ng.$rootScope.Scope();
16111                    scope.a = 1;
16112                    scope.b = 2;
16113
16114                    expect(scope.$eval('a+b')).toEqual(3);
16115                    expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16116                * ```
16117                *
16118                * @param {(string|function())=} expression An angular expression to be executed.
16119                *
16120                *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16121                *    - `function(scope)`: execute the function with the current `scope` parameter.
16122                *
16123                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16124                * @returns {*} The result of evaluating the expression.
16125                */
16126               $eval: function(expr, locals) {
16127                 return $parse(expr)(this, locals);
16128               },
16129
16130               /**
16131                * @ngdoc method
16132                * @name $rootScope.Scope#$evalAsync
16133                * @kind function
16134                *
16135                * @description
16136                * Executes the expression on the current scope at a later point in time.
16137                *
16138                * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16139                * that:
16140                *
16141                *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16142                *     rendering).
16143                *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16144                *     `expression` execution.
16145                *
16146                * Any exceptions from the execution of the expression are forwarded to the
16147                * {@link ng.$exceptionHandler $exceptionHandler} service.
16148                *
16149                * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16150                * will be scheduled. However, it is encouraged to always call code that changes the model
16151                * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16152                *
16153                * @param {(string|function())=} expression An angular expression to be executed.
16154                *
16155                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16156                *    - `function(scope)`: execute the function with the current `scope` parameter.
16157                *
16158                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16159                */
16160               $evalAsync: function(expr, locals) {
16161                 // if we are outside of an $digest loop and this is the first time we are scheduling async
16162                 // task also schedule async auto-flush
16163                 if (!$rootScope.$$phase && !asyncQueue.length) {
16164                   $browser.defer(function() {
16165                     if (asyncQueue.length) {
16166                       $rootScope.$digest();
16167                     }
16168                   });
16169                 }
16170
16171                 asyncQueue.push({scope: this, expression: expr, locals: locals});
16172               },
16173
16174               $$postDigest: function(fn) {
16175                 postDigestQueue.push(fn);
16176               },
16177
16178               /**
16179                * @ngdoc method
16180                * @name $rootScope.Scope#$apply
16181                * @kind function
16182                *
16183                * @description
16184                * `$apply()` is used to execute an expression in angular from outside of the angular
16185                * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16186                * Because we are calling into the angular framework we need to perform proper scope life
16187                * cycle of {@link ng.$exceptionHandler exception handling},
16188                * {@link ng.$rootScope.Scope#$digest executing watches}.
16189                *
16190                * ## Life cycle
16191                *
16192                * # Pseudo-Code of `$apply()`
16193                * ```js
16194                    function $apply(expr) {
16195                      try {
16196                        return $eval(expr);
16197                      } catch (e) {
16198                        $exceptionHandler(e);
16199                      } finally {
16200                        $root.$digest();
16201                      }
16202                    }
16203                * ```
16204                *
16205                *
16206                * Scope's `$apply()` method transitions through the following stages:
16207                *
16208                * 1. The {@link guide/expression expression} is executed using the
16209                *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16210                * 2. Any exceptions from the execution of the expression are forwarded to the
16211                *    {@link ng.$exceptionHandler $exceptionHandler} service.
16212                * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16213                *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16214                *
16215                *
16216                * @param {(string|function())=} exp An angular expression to be executed.
16217                *
16218                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16219                *    - `function(scope)`: execute the function with current `scope` parameter.
16220                *
16221                * @returns {*} The result of evaluating the expression.
16222                */
16223               $apply: function(expr) {
16224                 try {
16225                   beginPhase('$apply');
16226                   try {
16227                     return this.$eval(expr);
16228                   } finally {
16229                     clearPhase();
16230                   }
16231                 } catch (e) {
16232                   $exceptionHandler(e);
16233                 } finally {
16234                   try {
16235                     $rootScope.$digest();
16236                   } catch (e) {
16237                     $exceptionHandler(e);
16238                     throw e;
16239                   }
16240                 }
16241               },
16242
16243               /**
16244                * @ngdoc method
16245                * @name $rootScope.Scope#$applyAsync
16246                * @kind function
16247                *
16248                * @description
16249                * Schedule the invocation of $apply to occur at a later time. The actual time difference
16250                * varies across browsers, but is typically around ~10 milliseconds.
16251                *
16252                * This can be used to queue up multiple expressions which need to be evaluated in the same
16253                * digest.
16254                *
16255                * @param {(string|function())=} exp An angular expression to be executed.
16256                *
16257                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16258                *    - `function(scope)`: execute the function with current `scope` parameter.
16259                */
16260               $applyAsync: function(expr) {
16261                 var scope = this;
16262                 expr && applyAsyncQueue.push($applyAsyncExpression);
16263                 scheduleApplyAsync();
16264
16265                 function $applyAsyncExpression() {
16266                   scope.$eval(expr);
16267                 }
16268               },
16269
16270               /**
16271                * @ngdoc method
16272                * @name $rootScope.Scope#$on
16273                * @kind function
16274                *
16275                * @description
16276                * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16277                * discussion of event life cycle.
16278                *
16279                * The event listener function format is: `function(event, args...)`. The `event` object
16280                * passed into the listener has the following attributes:
16281                *
16282                *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16283                *     `$broadcast`-ed.
16284                *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16285                *     event propagates through the scope hierarchy, this property is set to null.
16286                *   - `name` - `{string}`: name of the event.
16287                *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16288                *     further event propagation (available only for events that were `$emit`-ed).
16289                *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16290                *     to true.
16291                *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16292                *
16293                * @param {string} name Event name to listen on.
16294                * @param {function(event, ...args)} listener Function to call when the event is emitted.
16295                * @returns {function()} Returns a deregistration function for this listener.
16296                */
16297               $on: function(name, listener) {
16298                 var namedListeners = this.$$listeners[name];
16299                 if (!namedListeners) {
16300                   this.$$listeners[name] = namedListeners = [];
16301                 }
16302                 namedListeners.push(listener);
16303
16304                 var current = this;
16305                 do {
16306                   if (!current.$$listenerCount[name]) {
16307                     current.$$listenerCount[name] = 0;
16308                   }
16309                   current.$$listenerCount[name]++;
16310                 } while ((current = current.$parent));
16311
16312                 var self = this;
16313                 return function() {
16314                   var indexOfListener = namedListeners.indexOf(listener);
16315                   if (indexOfListener !== -1) {
16316                     namedListeners[indexOfListener] = null;
16317                     decrementListenerCount(self, 1, name);
16318                   }
16319                 };
16320               },
16321
16322
16323               /**
16324                * @ngdoc method
16325                * @name $rootScope.Scope#$emit
16326                * @kind function
16327                *
16328                * @description
16329                * Dispatches an event `name` upwards through the scope hierarchy notifying the
16330                * registered {@link ng.$rootScope.Scope#$on} listeners.
16331                *
16332                * The event life cycle starts at the scope on which `$emit` was called. All
16333                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16334                * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16335                * registered listeners along the way. The event will stop propagating if one of the listeners
16336                * cancels it.
16337                *
16338                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16339                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16340                *
16341                * @param {string} name Event name to emit.
16342                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16343                * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16344                */
16345               $emit: function(name, args) {
16346                 var empty = [],
16347                     namedListeners,
16348                     scope = this,
16349                     stopPropagation = false,
16350                     event = {
16351                       name: name,
16352                       targetScope: scope,
16353                       stopPropagation: function() {stopPropagation = true;},
16354                       preventDefault: function() {
16355                         event.defaultPrevented = true;
16356                       },
16357                       defaultPrevented: false
16358                     },
16359                     listenerArgs = concat([event], arguments, 1),
16360                     i, length;
16361
16362                 do {
16363                   namedListeners = scope.$$listeners[name] || empty;
16364                   event.currentScope = scope;
16365                   for (i = 0, length = namedListeners.length; i < length; i++) {
16366
16367                     // if listeners were deregistered, defragment the array
16368                     if (!namedListeners[i]) {
16369                       namedListeners.splice(i, 1);
16370                       i--;
16371                       length--;
16372                       continue;
16373                     }
16374                     try {
16375                       //allow all listeners attached to the current scope to run
16376                       namedListeners[i].apply(null, listenerArgs);
16377                     } catch (e) {
16378                       $exceptionHandler(e);
16379                     }
16380                   }
16381                   //if any listener on the current scope stops propagation, prevent bubbling
16382                   if (stopPropagation) {
16383                     event.currentScope = null;
16384                     return event;
16385                   }
16386                   //traverse upwards
16387                   scope = scope.$parent;
16388                 } while (scope);
16389
16390                 event.currentScope = null;
16391
16392                 return event;
16393               },
16394
16395
16396               /**
16397                * @ngdoc method
16398                * @name $rootScope.Scope#$broadcast
16399                * @kind function
16400                *
16401                * @description
16402                * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16403                * registered {@link ng.$rootScope.Scope#$on} listeners.
16404                *
16405                * The event life cycle starts at the scope on which `$broadcast` was called. All
16406                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16407                * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16408                * scope and calls all registered listeners along the way. The event cannot be canceled.
16409                *
16410                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16411                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16412                *
16413                * @param {string} name Event name to broadcast.
16414                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16415                * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16416                */
16417               $broadcast: function(name, args) {
16418                 var target = this,
16419                     current = target,
16420                     next = target,
16421                     event = {
16422                       name: name,
16423                       targetScope: target,
16424                       preventDefault: function() {
16425                         event.defaultPrevented = true;
16426                       },
16427                       defaultPrevented: false
16428                     };
16429
16430                 if (!target.$$listenerCount[name]) return event;
16431
16432                 var listenerArgs = concat([event], arguments, 1),
16433                     listeners, i, length;
16434
16435                 //down while you can, then up and next sibling or up and next sibling until back at root
16436                 while ((current = next)) {
16437                   event.currentScope = current;
16438                   listeners = current.$$listeners[name] || [];
16439                   for (i = 0, length = listeners.length; i < length; i++) {
16440                     // if listeners were deregistered, defragment the array
16441                     if (!listeners[i]) {
16442                       listeners.splice(i, 1);
16443                       i--;
16444                       length--;
16445                       continue;
16446                     }
16447
16448                     try {
16449                       listeners[i].apply(null, listenerArgs);
16450                     } catch (e) {
16451                       $exceptionHandler(e);
16452                     }
16453                   }
16454
16455                   // Insanity Warning: scope depth-first traversal
16456                   // yes, this code is a bit crazy, but it works and we have tests to prove it!
16457                   // this piece should be kept in sync with the traversal in $digest
16458                   // (though it differs due to having the extra check for $$listenerCount)
16459                   if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16460                       (current !== target && current.$$nextSibling)))) {
16461                     while (current !== target && !(next = current.$$nextSibling)) {
16462                       current = current.$parent;
16463                     }
16464                   }
16465                 }
16466
16467                 event.currentScope = null;
16468                 return event;
16469               }
16470             };
16471
16472             var $rootScope = new Scope();
16473
16474             //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16475             var asyncQueue = $rootScope.$$asyncQueue = [];
16476             var postDigestQueue = $rootScope.$$postDigestQueue = [];
16477             var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16478
16479             return $rootScope;
16480
16481
16482             function beginPhase(phase) {
16483               if ($rootScope.$$phase) {
16484                 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16485               }
16486
16487               $rootScope.$$phase = phase;
16488             }
16489
16490             function clearPhase() {
16491               $rootScope.$$phase = null;
16492             }
16493
16494             function incrementWatchersCount(current, count) {
16495               do {
16496                 current.$$watchersCount += count;
16497               } while ((current = current.$parent));
16498             }
16499
16500             function decrementListenerCount(current, count, name) {
16501               do {
16502                 current.$$listenerCount[name] -= count;
16503
16504                 if (current.$$listenerCount[name] === 0) {
16505                   delete current.$$listenerCount[name];
16506                 }
16507               } while ((current = current.$parent));
16508             }
16509
16510             /**
16511              * function used as an initial value for watchers.
16512              * because it's unique we can easily tell it apart from other values
16513              */
16514             function initWatchVal() {}
16515
16516             function flushApplyAsync() {
16517               while (applyAsyncQueue.length) {
16518                 try {
16519                   applyAsyncQueue.shift()();
16520                 } catch (e) {
16521                   $exceptionHandler(e);
16522                 }
16523               }
16524               applyAsyncId = null;
16525             }
16526
16527             function scheduleApplyAsync() {
16528               if (applyAsyncId === null) {
16529                 applyAsyncId = $browser.defer(function() {
16530                   $rootScope.$apply(flushApplyAsync);
16531                 });
16532               }
16533             }
16534           }];
16535         }
16536
16537         /**
16538          * @description
16539          * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16540          */
16541         function $$SanitizeUriProvider() {
16542           var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16543             imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16544
16545           /**
16546            * @description
16547            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16548            * urls during a[href] sanitization.
16549            *
16550            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16551            *
16552            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16553            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16554            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16555            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16556            *
16557            * @param {RegExp=} regexp New regexp to whitelist urls with.
16558            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16559            *    chaining otherwise.
16560            */
16561           this.aHrefSanitizationWhitelist = function(regexp) {
16562             if (isDefined(regexp)) {
16563               aHrefSanitizationWhitelist = regexp;
16564               return this;
16565             }
16566             return aHrefSanitizationWhitelist;
16567           };
16568
16569
16570           /**
16571            * @description
16572            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16573            * urls during img[src] sanitization.
16574            *
16575            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16576            *
16577            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16578            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16579            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16580            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16581            *
16582            * @param {RegExp=} regexp New regexp to whitelist urls with.
16583            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16584            *    chaining otherwise.
16585            */
16586           this.imgSrcSanitizationWhitelist = function(regexp) {
16587             if (isDefined(regexp)) {
16588               imgSrcSanitizationWhitelist = regexp;
16589               return this;
16590             }
16591             return imgSrcSanitizationWhitelist;
16592           };
16593
16594           this.$get = function() {
16595             return function sanitizeUri(uri, isImage) {
16596               var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16597               var normalizedVal;
16598               normalizedVal = urlResolve(uri).href;
16599               if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16600                 return 'unsafe:' + normalizedVal;
16601               }
16602               return uri;
16603             };
16604           };
16605         }
16606
16607         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16608          *     Any commits to this file should be reviewed with security in mind.  *
16609          *   Changes to this file can potentially create security vulnerabilities. *
16610          *          An approval from 2 Core members with history of modifying      *
16611          *                         this file is required.                          *
16612          *                                                                         *
16613          *  Does the change somehow allow for arbitrary javascript to be executed? *
16614          *    Or allows for someone to change the prototype of built-in objects?   *
16615          *     Or gives undesired access to variables likes document or window?    *
16616          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16617
16618         var $sceMinErr = minErr('$sce');
16619
16620         var SCE_CONTEXTS = {
16621           HTML: 'html',
16622           CSS: 'css',
16623           URL: 'url',
16624           // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16625           // url.  (e.g. ng-include, script src, templateUrl)
16626           RESOURCE_URL: 'resourceUrl',
16627           JS: 'js'
16628         };
16629
16630         // Helper functions follow.
16631
16632         function adjustMatcher(matcher) {
16633           if (matcher === 'self') {
16634             return matcher;
16635           } else if (isString(matcher)) {
16636             // Strings match exactly except for 2 wildcards - '*' and '**'.
16637             // '*' matches any character except those from the set ':/.?&'.
16638             // '**' matches any character (like .* in a RegExp).
16639             // More than 2 *'s raises an error as it's ill defined.
16640             if (matcher.indexOf('***') > -1) {
16641               throw $sceMinErr('iwcard',
16642                   'Illegal sequence *** in string matcher.  String: {0}', matcher);
16643             }
16644             matcher = escapeForRegexp(matcher).
16645                           replace('\\*\\*', '.*').
16646                           replace('\\*', '[^:/.?&;]*');
16647             return new RegExp('^' + matcher + '$');
16648           } else if (isRegExp(matcher)) {
16649             // The only other type of matcher allowed is a Regexp.
16650             // Match entire URL / disallow partial matches.
16651             // Flags are reset (i.e. no global, ignoreCase or multiline)
16652             return new RegExp('^' + matcher.source + '$');
16653           } else {
16654             throw $sceMinErr('imatcher',
16655                 'Matchers may only be "self", string patterns or RegExp objects');
16656           }
16657         }
16658
16659
16660         function adjustMatchers(matchers) {
16661           var adjustedMatchers = [];
16662           if (isDefined(matchers)) {
16663             forEach(matchers, function(matcher) {
16664               adjustedMatchers.push(adjustMatcher(matcher));
16665             });
16666           }
16667           return adjustedMatchers;
16668         }
16669
16670
16671         /**
16672          * @ngdoc service
16673          * @name $sceDelegate
16674          * @kind function
16675          *
16676          * @description
16677          *
16678          * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16679          * Contextual Escaping (SCE)} services to AngularJS.
16680          *
16681          * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16682          * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16683          * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16684          * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16685          * work because `$sce` delegates to `$sceDelegate` for these operations.
16686          *
16687          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16688          *
16689          * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16690          * can override it completely to change the behavior of `$sce`, the common case would
16691          * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16692          * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16693          * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16694          * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16695          * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16696          */
16697
16698         /**
16699          * @ngdoc provider
16700          * @name $sceDelegateProvider
16701          * @description
16702          *
16703          * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16704          * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16705          * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16706          * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16707          * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16708          *
16709          * For the general details about this service in Angular, read the main page for {@link ng.$sce
16710          * Strict Contextual Escaping (SCE)}.
16711          *
16712          * **Example**:  Consider the following case. <a name="example"></a>
16713          *
16714          * - your app is hosted at url `http://myapp.example.com/`
16715          * - but some of your templates are hosted on other domains you control such as
16716          *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16717          * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16718          *
16719          * Here is what a secure configuration for this scenario might look like:
16720          *
16721          * ```
16722          *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16723          *    $sceDelegateProvider.resourceUrlWhitelist([
16724          *      // Allow same origin resource loads.
16725          *      'self',
16726          *      // Allow loading from our assets domain.  Notice the difference between * and **.
16727          *      'http://srv*.assets.example.com/**'
16728          *    ]);
16729          *
16730          *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16731          *    $sceDelegateProvider.resourceUrlBlacklist([
16732          *      'http://myapp.example.com/clickThru**'
16733          *    ]);
16734          *  });
16735          * ```
16736          */
16737
16738         function $SceDelegateProvider() {
16739           this.SCE_CONTEXTS = SCE_CONTEXTS;
16740
16741           // Resource URLs can also be trusted by policy.
16742           var resourceUrlWhitelist = ['self'],
16743               resourceUrlBlacklist = [];
16744
16745           /**
16746            * @ngdoc method
16747            * @name $sceDelegateProvider#resourceUrlWhitelist
16748            * @kind function
16749            *
16750            * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16751            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16752            *     changes to the array are ignored.
16753            *
16754            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16755            *     allowed in this array.
16756            *
16757            *     Note: **an empty whitelist array will block all URLs**!
16758            *
16759            * @return {Array} the currently set whitelist array.
16760            *
16761            * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16762            * same origin resource requests.
16763            *
16764            * @description
16765            * Sets/Gets the whitelist of trusted resource URLs.
16766            */
16767           this.resourceUrlWhitelist = function(value) {
16768             if (arguments.length) {
16769               resourceUrlWhitelist = adjustMatchers(value);
16770             }
16771             return resourceUrlWhitelist;
16772           };
16773
16774           /**
16775            * @ngdoc method
16776            * @name $sceDelegateProvider#resourceUrlBlacklist
16777            * @kind function
16778            *
16779            * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16780            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16781            *     changes to the array are ignored.
16782            *
16783            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16784            *     allowed in this array.
16785            *
16786            *     The typical usage for the blacklist is to **block
16787            *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16788            *     these would otherwise be trusted but actually return content from the redirected domain.
16789            *
16790            *     Finally, **the blacklist overrides the whitelist** and has the final say.
16791            *
16792            * @return {Array} the currently set blacklist array.
16793            *
16794            * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16795            * is no blacklist.)
16796            *
16797            * @description
16798            * Sets/Gets the blacklist of trusted resource URLs.
16799            */
16800
16801           this.resourceUrlBlacklist = function(value) {
16802             if (arguments.length) {
16803               resourceUrlBlacklist = adjustMatchers(value);
16804             }
16805             return resourceUrlBlacklist;
16806           };
16807
16808           this.$get = ['$injector', function($injector) {
16809
16810             var htmlSanitizer = function htmlSanitizer(html) {
16811               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16812             };
16813
16814             if ($injector.has('$sanitize')) {
16815               htmlSanitizer = $injector.get('$sanitize');
16816             }
16817
16818
16819             function matchUrl(matcher, parsedUrl) {
16820               if (matcher === 'self') {
16821                 return urlIsSameOrigin(parsedUrl);
16822               } else {
16823                 // definitely a regex.  See adjustMatchers()
16824                 return !!matcher.exec(parsedUrl.href);
16825               }
16826             }
16827
16828             function isResourceUrlAllowedByPolicy(url) {
16829               var parsedUrl = urlResolve(url.toString());
16830               var i, n, allowed = false;
16831               // Ensure that at least one item from the whitelist allows this url.
16832               for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16833                 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16834                   allowed = true;
16835                   break;
16836                 }
16837               }
16838               if (allowed) {
16839                 // Ensure that no item from the blacklist blocked this url.
16840                 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16841                   if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16842                     allowed = false;
16843                     break;
16844                   }
16845                 }
16846               }
16847               return allowed;
16848             }
16849
16850             function generateHolderType(Base) {
16851               var holderType = function TrustedValueHolderType(trustedValue) {
16852                 this.$$unwrapTrustedValue = function() {
16853                   return trustedValue;
16854                 };
16855               };
16856               if (Base) {
16857                 holderType.prototype = new Base();
16858               }
16859               holderType.prototype.valueOf = function sceValueOf() {
16860                 return this.$$unwrapTrustedValue();
16861               };
16862               holderType.prototype.toString = function sceToString() {
16863                 return this.$$unwrapTrustedValue().toString();
16864               };
16865               return holderType;
16866             }
16867
16868             var trustedValueHolderBase = generateHolderType(),
16869                 byType = {};
16870
16871             byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16872             byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16873             byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16874             byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16875             byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16876
16877             /**
16878              * @ngdoc method
16879              * @name $sceDelegate#trustAs
16880              *
16881              * @description
16882              * Returns an object that is trusted by angular for use in specified strict
16883              * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16884              * attribute interpolation, any dom event binding attribute interpolation
16885              * such as for onclick,  etc.) that uses the provided value.
16886              * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16887              *
16888              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16889              *   resourceUrl, html, js and css.
16890              * @param {*} value The value that that should be considered trusted/safe.
16891              * @returns {*} A value that can be used to stand in for the provided `value` in places
16892              * where Angular expects a $sce.trustAs() return value.
16893              */
16894             function trustAs(type, trustedValue) {
16895               var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16896               if (!Constructor) {
16897                 throw $sceMinErr('icontext',
16898                     'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16899                     type, trustedValue);
16900               }
16901               if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16902                 return trustedValue;
16903               }
16904               // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16905               // mutable objects, we ensure here that the value passed in is actually a string.
16906               if (typeof trustedValue !== 'string') {
16907                 throw $sceMinErr('itype',
16908                     'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16909                     type);
16910               }
16911               return new Constructor(trustedValue);
16912             }
16913
16914             /**
16915              * @ngdoc method
16916              * @name $sceDelegate#valueOf
16917              *
16918              * @description
16919              * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16920              * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16921              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16922              *
16923              * If the passed parameter is not a value that had been returned by {@link
16924              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16925              *
16926              * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16927              *      call or anything else.
16928              * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16929              *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16930              *     `value` unchanged.
16931              */
16932             function valueOf(maybeTrusted) {
16933               if (maybeTrusted instanceof trustedValueHolderBase) {
16934                 return maybeTrusted.$$unwrapTrustedValue();
16935               } else {
16936                 return maybeTrusted;
16937               }
16938             }
16939
16940             /**
16941              * @ngdoc method
16942              * @name $sceDelegate#getTrusted
16943              *
16944              * @description
16945              * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16946              * returns the originally supplied value if the queried context type is a supertype of the
16947              * created type.  If this condition isn't satisfied, throws an exception.
16948              *
16949              * @param {string} type The kind of context in which this value is to be used.
16950              * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16951              *     `$sceDelegate.trustAs`} call.
16952              * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16953              *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16954              */
16955             function getTrusted(type, maybeTrusted) {
16956               if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16957                 return maybeTrusted;
16958               }
16959               var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16960               if (constructor && maybeTrusted instanceof constructor) {
16961                 return maybeTrusted.$$unwrapTrustedValue();
16962               }
16963               // If we get here, then we may only take one of two actions.
16964               // 1. sanitize the value for the requested type, or
16965               // 2. throw an exception.
16966               if (type === SCE_CONTEXTS.RESOURCE_URL) {
16967                 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16968                   return maybeTrusted;
16969                 } else {
16970                   throw $sceMinErr('insecurl',
16971                       'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16972                       maybeTrusted.toString());
16973                 }
16974               } else if (type === SCE_CONTEXTS.HTML) {
16975                 return htmlSanitizer(maybeTrusted);
16976               }
16977               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16978             }
16979
16980             return { trustAs: trustAs,
16981                      getTrusted: getTrusted,
16982                      valueOf: valueOf };
16983           }];
16984         }
16985
16986
16987         /**
16988          * @ngdoc provider
16989          * @name $sceProvider
16990          * @description
16991          *
16992          * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16993          * -   enable/disable Strict Contextual Escaping (SCE) in a module
16994          * -   override the default implementation with a custom delegate
16995          *
16996          * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16997          */
16998
16999         /* jshint maxlen: false*/
17000
17001         /**
17002          * @ngdoc service
17003          * @name $sce
17004          * @kind function
17005          *
17006          * @description
17007          *
17008          * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17009          *
17010          * # Strict Contextual Escaping
17011          *
17012          * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17013          * contexts to result in a value that is marked as safe to use for that context.  One example of
17014          * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17015          * to these contexts as privileged or SCE contexts.
17016          *
17017          * As of version 1.2, Angular ships with SCE enabled by default.
17018          *
17019          * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17020          * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17021          * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17022          * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17023          * to the top of your HTML document.
17024          *
17025          * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17026          * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17027          *
17028          * Here's an example of a binding in a privileged context:
17029          *
17030          * ```
17031          * <input ng-model="userHtml" aria-label="User input">
17032          * <div ng-bind-html="userHtml"></div>
17033          * ```
17034          *
17035          * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17036          * disabled, this application allows the user to render arbitrary HTML into the DIV.
17037          * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17038          * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17039          * security vulnerabilities.)
17040          *
17041          * For the case of HTML, you might use a library, either on the client side, or on the server side,
17042          * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17043          *
17044          * How would you ensure that every place that used these types of bindings was bound to a value that
17045          * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17046          * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17047          * properties/fields and forgot to update the binding to the sanitized value?
17048          *
17049          * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17050          * determine that something explicitly says it's safe to use a value for binding in that
17051          * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17052          * for those values that you can easily tell are safe - because they were received from your server,
17053          * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17054          * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17055          * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17056          *
17057          * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17058          * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17059          * obtain values that will be accepted by SCE / privileged contexts.
17060          *
17061          *
17062          * ## How does it work?
17063          *
17064          * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17065          * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17066          * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17067          * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17068          *
17069          * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17070          * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17071          * simplified):
17072          *
17073          * ```
17074          * var ngBindHtmlDirective = ['$sce', function($sce) {
17075          *   return function(scope, element, attr) {
17076          *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17077          *       element.html(value || '');
17078          *     });
17079          *   };
17080          * }];
17081          * ```
17082          *
17083          * ## Impact on loading templates
17084          *
17085          * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17086          * `templateUrl`'s specified by {@link guide/directive directives}.
17087          *
17088          * By default, Angular only loads templates from the same domain and protocol as the application
17089          * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17090          * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17091          * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17092          * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17093          *
17094          * *Please note*:
17095          * The browser's
17096          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17097          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17098          * policy apply in addition to this and may further restrict whether the template is successfully
17099          * loaded.  This means that without the right CORS policy, loading templates from a different domain
17100          * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17101          * browsers.
17102          *
17103          * ## This feels like too much overhead
17104          *
17105          * It's important to remember that SCE only applies to interpolation expressions.
17106          *
17107          * If your expressions are constant literals, they're automatically trusted and you don't need to
17108          * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17109          * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17110          *
17111          * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17112          * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17113          *
17114          * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17115          * templates in `ng-include` from your application's domain without having to even know about SCE.
17116          * It blocks loading templates from other domains or loading templates over http from an https
17117          * served document.  You can change these by setting your own custom {@link
17118          * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17119          * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17120          *
17121          * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17122          * application that's secure and can be audited to verify that with much more ease than bolting
17123          * security onto an application later.
17124          *
17125          * <a name="contexts"></a>
17126          * ## What trusted context types are supported?
17127          *
17128          * | Context             | Notes          |
17129          * |---------------------|----------------|
17130          * | `$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. |
17131          * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17132          * | `$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. |
17133          * | `$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. |
17134          * | `$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. |
17135          *
17136          * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17137          *
17138          *  Each element in these arrays must be one of the following:
17139          *
17140          *  - **'self'**
17141          *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17142          *      domain** as the application document using the **same protocol**.
17143          *  - **String** (except the special value `'self'`)
17144          *    - The string is matched against the full *normalized / absolute URL* of the resource
17145          *      being tested (substring matches are not good enough.)
17146          *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17147          *      match themselves.
17148          *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17149          *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17150          *      in a whitelist.
17151          *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17152          *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17153          *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17154          *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17155          *      http://foo.example.com/templates/**).
17156          *  - **RegExp** (*see caveat below*)
17157          *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17158          *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17159          *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17160          *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17161          *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17162          *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17163          *      is highly recommended to use the string patterns and only fall back to regular expressions
17164          *      as a last resort.
17165          *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17166          *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17167          *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17168          *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17169          *    - If you are generating your JavaScript from some other templating engine (not
17170          *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17171          *      remember to escape your regular expression (and be aware that you might need more than
17172          *      one level of escaping depending on your templating engine and the way you interpolated
17173          *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17174          *      enough before coding your own.  E.g. Ruby has
17175          *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17176          *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17177          *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17178          *      Closure library's [goog.string.regExpEscape(s)](
17179          *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17180          *
17181          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17182          *
17183          * ## Show me an example using SCE.
17184          *
17185          * <example module="mySceApp" deps="angular-sanitize.js">
17186          * <file name="index.html">
17187          *   <div ng-controller="AppController as myCtrl">
17188          *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17189          *     <b>User comments</b><br>
17190          *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17191          *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17192          *     exploit.
17193          *     <div class="well">
17194          *       <div ng-repeat="userComment in myCtrl.userComments">
17195          *         <b>{{userComment.name}}</b>:
17196          *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17197          *         <br>
17198          *       </div>
17199          *     </div>
17200          *   </div>
17201          * </file>
17202          *
17203          * <file name="script.js">
17204          *   angular.module('mySceApp', ['ngSanitize'])
17205          *     .controller('AppController', ['$http', '$templateCache', '$sce',
17206          *       function($http, $templateCache, $sce) {
17207          *         var self = this;
17208          *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17209          *           self.userComments = userComments;
17210          *         });
17211          *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17212          *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17213          *             'sanitization.&quot;">Hover over this text.</span>');
17214          *       }]);
17215          * </file>
17216          *
17217          * <file name="test_data.json">
17218          * [
17219          *   { "name": "Alice",
17220          *     "htmlComment":
17221          *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17222          *   },
17223          *   { "name": "Bob",
17224          *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17225          *   }
17226          * ]
17227          * </file>
17228          *
17229          * <file name="protractor.js" type="protractor">
17230          *   describe('SCE doc demo', function() {
17231          *     it('should sanitize untrusted values', function() {
17232          *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17233          *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17234          *     });
17235          *
17236          *     it('should NOT sanitize explicitly trusted values', function() {
17237          *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17238          *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17239          *           'sanitization.&quot;">Hover over this text.</span>');
17240          *     });
17241          *   });
17242          * </file>
17243          * </example>
17244          *
17245          *
17246          *
17247          * ## Can I disable SCE completely?
17248          *
17249          * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17250          * for little coding overhead.  It will be much harder to take an SCE disabled application and
17251          * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17252          * for cases where you have a lot of existing code that was written before SCE was introduced and
17253          * you're migrating them a module at a time.
17254          *
17255          * That said, here's how you can completely disable SCE:
17256          *
17257          * ```
17258          * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17259          *   // Completely disable SCE.  For demonstration purposes only!
17260          *   // Do not use in new projects.
17261          *   $sceProvider.enabled(false);
17262          * });
17263          * ```
17264          *
17265          */
17266         /* jshint maxlen: 100 */
17267
17268         function $SceProvider() {
17269           var enabled = true;
17270
17271           /**
17272            * @ngdoc method
17273            * @name $sceProvider#enabled
17274            * @kind function
17275            *
17276            * @param {boolean=} value If provided, then enables/disables SCE.
17277            * @return {boolean} true if SCE is enabled, false otherwise.
17278            *
17279            * @description
17280            * Enables/disables SCE and returns the current value.
17281            */
17282           this.enabled = function(value) {
17283             if (arguments.length) {
17284               enabled = !!value;
17285             }
17286             return enabled;
17287           };
17288
17289
17290           /* Design notes on the default implementation for SCE.
17291            *
17292            * The API contract for the SCE delegate
17293            * -------------------------------------
17294            * The SCE delegate object must provide the following 3 methods:
17295            *
17296            * - trustAs(contextEnum, value)
17297            *     This method is used to tell the SCE service that the provided value is OK to use in the
17298            *     contexts specified by contextEnum.  It must return an object that will be accepted by
17299            *     getTrusted() for a compatible contextEnum and return this value.
17300            *
17301            * - valueOf(value)
17302            *     For values that were not produced by trustAs(), return them as is.  For values that were
17303            *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17304            *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17305            *     such a value.
17306            *
17307            * - getTrusted(contextEnum, value)
17308            *     This function should return the a value that is safe to use in the context specified by
17309            *     contextEnum or throw and exception otherwise.
17310            *
17311            * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17312            * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17313            * instance, an implementation could maintain a registry of all trusted objects by context.  In
17314            * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17315            * return the same object passed in if it was found in the registry under a compatible context or
17316            * throw an exception otherwise.  An implementation might only wrap values some of the time based
17317            * on some criteria.  getTrusted() might return a value and not throw an exception for special
17318            * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17319            *
17320            *
17321            * A note on the inheritance model for SCE contexts
17322            * ------------------------------------------------
17323            * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17324            * is purely an implementation details.
17325            *
17326            * The contract is simply this:
17327            *
17328            *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17329            *     will also succeed.
17330            *
17331            * Inheritance happens to capture this in a natural way.  In some future, we
17332            * may not use inheritance anymore.  That is OK because no code outside of
17333            * sce.js and sceSpecs.js would need to be aware of this detail.
17334            */
17335
17336           this.$get = ['$parse', '$sceDelegate', function(
17337                         $parse,   $sceDelegate) {
17338             // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17339             // the "expression(javascript expression)" syntax which is insecure.
17340             if (enabled && msie < 8) {
17341               throw $sceMinErr('iequirks',
17342                 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17343                 'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17344                 'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17345             }
17346
17347             var sce = shallowCopy(SCE_CONTEXTS);
17348
17349             /**
17350              * @ngdoc method
17351              * @name $sce#isEnabled
17352              * @kind function
17353              *
17354              * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17355              * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17356              *
17357              * @description
17358              * Returns a boolean indicating if SCE is enabled.
17359              */
17360             sce.isEnabled = function() {
17361               return enabled;
17362             };
17363             sce.trustAs = $sceDelegate.trustAs;
17364             sce.getTrusted = $sceDelegate.getTrusted;
17365             sce.valueOf = $sceDelegate.valueOf;
17366
17367             if (!enabled) {
17368               sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17369               sce.valueOf = identity;
17370             }
17371
17372             /**
17373              * @ngdoc method
17374              * @name $sce#parseAs
17375              *
17376              * @description
17377              * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17378              * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17379              * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17380              * *result*)}
17381              *
17382              * @param {string} type The kind of SCE context in which this result will be used.
17383              * @param {string} expression String expression to compile.
17384              * @returns {function(context, locals)} a function which represents the compiled expression:
17385              *
17386              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17387              *      are evaluated against (typically a scope object).
17388              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17389              *      `context`.
17390              */
17391             sce.parseAs = function sceParseAs(type, expr) {
17392               var parsed = $parse(expr);
17393               if (parsed.literal && parsed.constant) {
17394                 return parsed;
17395               } else {
17396                 return $parse(expr, function(value) {
17397                   return sce.getTrusted(type, value);
17398                 });
17399               }
17400             };
17401
17402             /**
17403              * @ngdoc method
17404              * @name $sce#trustAs
17405              *
17406              * @description
17407              * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17408              * returns an object that is trusted by angular for use in specified strict contextual
17409              * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17410              * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17411              * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17412              * escaping.
17413              *
17414              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17415              *   resourceUrl, html, js and css.
17416              * @param {*} value The value that that should be considered trusted/safe.
17417              * @returns {*} A value that can be used to stand in for the provided `value` in places
17418              * where Angular expects a $sce.trustAs() return value.
17419              */
17420
17421             /**
17422              * @ngdoc method
17423              * @name $sce#trustAsHtml
17424              *
17425              * @description
17426              * Shorthand method.  `$sce.trustAsHtml(value)` →
17427              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17428              *
17429              * @param {*} value The value to trustAs.
17430              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17431              *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17432              *     only accept expressions that are either literal constants or are the
17433              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17434              */
17435
17436             /**
17437              * @ngdoc method
17438              * @name $sce#trustAsUrl
17439              *
17440              * @description
17441              * Shorthand method.  `$sce.trustAsUrl(value)` →
17442              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17443              *
17444              * @param {*} value The value to trustAs.
17445              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17446              *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17447              *     only accept expressions that are either literal constants or are the
17448              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17449              */
17450
17451             /**
17452              * @ngdoc method
17453              * @name $sce#trustAsResourceUrl
17454              *
17455              * @description
17456              * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17457              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17458              *
17459              * @param {*} value The value to trustAs.
17460              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17461              *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17462              *     only accept expressions that are either literal constants or are the return
17463              *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17464              */
17465
17466             /**
17467              * @ngdoc method
17468              * @name $sce#trustAsJs
17469              *
17470              * @description
17471              * Shorthand method.  `$sce.trustAsJs(value)` →
17472              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17473              *
17474              * @param {*} value The value to trustAs.
17475              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17476              *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17477              *     only accept expressions that are either literal constants or are the
17478              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17479              */
17480
17481             /**
17482              * @ngdoc method
17483              * @name $sce#getTrusted
17484              *
17485              * @description
17486              * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17487              * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17488              * originally supplied value if the queried context type is a supertype of the created type.
17489              * If this condition isn't satisfied, throws an exception.
17490              *
17491              * @param {string} type The kind of context in which this value is to be used.
17492              * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17493              *                         call.
17494              * @returns {*} The value the was originally provided to
17495              *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17496              *              Otherwise, throws an exception.
17497              */
17498
17499             /**
17500              * @ngdoc method
17501              * @name $sce#getTrustedHtml
17502              *
17503              * @description
17504              * Shorthand method.  `$sce.getTrustedHtml(value)` →
17505              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17506              *
17507              * @param {*} value The value to pass to `$sce.getTrusted`.
17508              * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17509              */
17510
17511             /**
17512              * @ngdoc method
17513              * @name $sce#getTrustedCss
17514              *
17515              * @description
17516              * Shorthand method.  `$sce.getTrustedCss(value)` →
17517              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17518              *
17519              * @param {*} value The value to pass to `$sce.getTrusted`.
17520              * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17521              */
17522
17523             /**
17524              * @ngdoc method
17525              * @name $sce#getTrustedUrl
17526              *
17527              * @description
17528              * Shorthand method.  `$sce.getTrustedUrl(value)` →
17529              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17530              *
17531              * @param {*} value The value to pass to `$sce.getTrusted`.
17532              * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17533              */
17534
17535             /**
17536              * @ngdoc method
17537              * @name $sce#getTrustedResourceUrl
17538              *
17539              * @description
17540              * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17541              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17542              *
17543              * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17544              * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17545              */
17546
17547             /**
17548              * @ngdoc method
17549              * @name $sce#getTrustedJs
17550              *
17551              * @description
17552              * Shorthand method.  `$sce.getTrustedJs(value)` →
17553              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17554              *
17555              * @param {*} value The value to pass to `$sce.getTrusted`.
17556              * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17557              */
17558
17559             /**
17560              * @ngdoc method
17561              * @name $sce#parseAsHtml
17562              *
17563              * @description
17564              * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17565              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17566              *
17567              * @param {string} expression String expression to compile.
17568              * @returns {function(context, locals)} a function which represents the compiled expression:
17569              *
17570              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17571              *      are evaluated against (typically a scope object).
17572              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17573              *      `context`.
17574              */
17575
17576             /**
17577              * @ngdoc method
17578              * @name $sce#parseAsCss
17579              *
17580              * @description
17581              * Shorthand method.  `$sce.parseAsCss(value)` →
17582              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17583              *
17584              * @param {string} expression String expression to compile.
17585              * @returns {function(context, locals)} a function which represents the compiled expression:
17586              *
17587              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17588              *      are evaluated against (typically a scope object).
17589              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17590              *      `context`.
17591              */
17592
17593             /**
17594              * @ngdoc method
17595              * @name $sce#parseAsUrl
17596              *
17597              * @description
17598              * Shorthand method.  `$sce.parseAsUrl(value)` →
17599              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17600              *
17601              * @param {string} expression String expression to compile.
17602              * @returns {function(context, locals)} a function which represents the compiled expression:
17603              *
17604              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17605              *      are evaluated against (typically a scope object).
17606              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17607              *      `context`.
17608              */
17609
17610             /**
17611              * @ngdoc method
17612              * @name $sce#parseAsResourceUrl
17613              *
17614              * @description
17615              * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17616              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17617              *
17618              * @param {string} expression String expression to compile.
17619              * @returns {function(context, locals)} a function which represents the compiled expression:
17620              *
17621              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17622              *      are evaluated against (typically a scope object).
17623              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17624              *      `context`.
17625              */
17626
17627             /**
17628              * @ngdoc method
17629              * @name $sce#parseAsJs
17630              *
17631              * @description
17632              * Shorthand method.  `$sce.parseAsJs(value)` →
17633              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17634              *
17635              * @param {string} expression String expression to compile.
17636              * @returns {function(context, locals)} a function which represents the compiled expression:
17637              *
17638              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17639              *      are evaluated against (typically a scope object).
17640              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17641              *      `context`.
17642              */
17643
17644             // Shorthand delegations.
17645             var parse = sce.parseAs,
17646                 getTrusted = sce.getTrusted,
17647                 trustAs = sce.trustAs;
17648
17649             forEach(SCE_CONTEXTS, function(enumValue, name) {
17650               var lName = lowercase(name);
17651               sce[camelCase("parse_as_" + lName)] = function(expr) {
17652                 return parse(enumValue, expr);
17653               };
17654               sce[camelCase("get_trusted_" + lName)] = function(value) {
17655                 return getTrusted(enumValue, value);
17656               };
17657               sce[camelCase("trust_as_" + lName)] = function(value) {
17658                 return trustAs(enumValue, value);
17659               };
17660             });
17661
17662             return sce;
17663           }];
17664         }
17665
17666         /**
17667          * !!! This is an undocumented "private" service !!!
17668          *
17669          * @name $sniffer
17670          * @requires $window
17671          * @requires $document
17672          *
17673          * @property {boolean} history Does the browser support html5 history api ?
17674          * @property {boolean} transitions Does the browser support CSS transition events ?
17675          * @property {boolean} animations Does the browser support CSS animation events ?
17676          *
17677          * @description
17678          * This is very simple implementation of testing browser's features.
17679          */
17680         function $SnifferProvider() {
17681           this.$get = ['$window', '$document', function($window, $document) {
17682             var eventSupport = {},
17683                 android =
17684                   toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17685                 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17686                 document = $document[0] || {},
17687                 vendorPrefix,
17688                 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17689                 bodyStyle = document.body && document.body.style,
17690                 transitions = false,
17691                 animations = false,
17692                 match;
17693
17694             if (bodyStyle) {
17695               for (var prop in bodyStyle) {
17696                 if (match = vendorRegex.exec(prop)) {
17697                   vendorPrefix = match[0];
17698                   vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17699                   break;
17700                 }
17701               }
17702
17703               if (!vendorPrefix) {
17704                 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17705               }
17706
17707               transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17708               animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17709
17710               if (android && (!transitions ||  !animations)) {
17711                 transitions = isString(bodyStyle.webkitTransition);
17712                 animations = isString(bodyStyle.webkitAnimation);
17713               }
17714             }
17715
17716
17717             return {
17718               // Android has history.pushState, but it does not update location correctly
17719               // so let's not use the history API at all.
17720               // http://code.google.com/p/android/issues/detail?id=17471
17721               // https://github.com/angular/angular.js/issues/904
17722
17723               // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17724               // so let's not use the history API also
17725               // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17726               // jshint -W018
17727               history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17728               // jshint +W018
17729               hasEvent: function(event) {
17730                 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17731                 // it. In particular the event is not fired when backspace or delete key are pressed or
17732                 // when cut operation is performed.
17733                 // IE10+ implements 'input' event but it erroneously fires under various situations,
17734                 // e.g. when placeholder changes, or a form is focused.
17735                 if (event === 'input' && msie <= 11) return false;
17736
17737                 if (isUndefined(eventSupport[event])) {
17738                   var divElm = document.createElement('div');
17739                   eventSupport[event] = 'on' + event in divElm;
17740                 }
17741
17742                 return eventSupport[event];
17743               },
17744               csp: csp(),
17745               vendorPrefix: vendorPrefix,
17746               transitions: transitions,
17747               animations: animations,
17748               android: android
17749             };
17750           }];
17751         }
17752
17753         var $compileMinErr = minErr('$compile');
17754
17755         /**
17756          * @ngdoc service
17757          * @name $templateRequest
17758          *
17759          * @description
17760          * The `$templateRequest` service runs security checks then downloads the provided template using
17761          * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17762          * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17763          * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17764          * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17765          * when `tpl` is of type string and `$templateCache` has the matching entry.
17766          *
17767          * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17768          * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17769          *
17770          * @return {Promise} a promise for the HTTP response data of the given URL.
17771          *
17772          * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17773          */
17774         function $TemplateRequestProvider() {
17775           this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17776             function handleRequestFn(tpl, ignoreRequestError) {
17777               handleRequestFn.totalPendingRequests++;
17778
17779               // We consider the template cache holds only trusted templates, so
17780               // there's no need to go through whitelisting again for keys that already
17781               // are included in there. This also makes Angular accept any script
17782               // directive, no matter its name. However, we still need to unwrap trusted
17783               // types.
17784               if (!isString(tpl) || !$templateCache.get(tpl)) {
17785                 tpl = $sce.getTrustedResourceUrl(tpl);
17786               }
17787
17788               var transformResponse = $http.defaults && $http.defaults.transformResponse;
17789
17790               if (isArray(transformResponse)) {
17791                 transformResponse = transformResponse.filter(function(transformer) {
17792                   return transformer !== defaultHttpResponseTransform;
17793                 });
17794               } else if (transformResponse === defaultHttpResponseTransform) {
17795                 transformResponse = null;
17796               }
17797
17798               var httpOptions = {
17799                 cache: $templateCache,
17800                 transformResponse: transformResponse
17801               };
17802
17803               return $http.get(tpl, httpOptions)
17804                 ['finally'](function() {
17805                   handleRequestFn.totalPendingRequests--;
17806                 })
17807                 .then(function(response) {
17808                   $templateCache.put(tpl, response.data);
17809                   return response.data;
17810                 }, handleError);
17811
17812               function handleError(resp) {
17813                 if (!ignoreRequestError) {
17814                   throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17815                     tpl, resp.status, resp.statusText);
17816                 }
17817                 return $q.reject(resp);
17818               }
17819             }
17820
17821             handleRequestFn.totalPendingRequests = 0;
17822
17823             return handleRequestFn;
17824           }];
17825         }
17826
17827         function $$TestabilityProvider() {
17828           this.$get = ['$rootScope', '$browser', '$location',
17829                function($rootScope,   $browser,   $location) {
17830
17831             /**
17832              * @name $testability
17833              *
17834              * @description
17835              * The private $$testability service provides a collection of methods for use when debugging
17836              * or by automated test and debugging tools.
17837              */
17838             var testability = {};
17839
17840             /**
17841              * @name $$testability#findBindings
17842              *
17843              * @description
17844              * Returns an array of elements that are bound (via ng-bind or {{}})
17845              * to expressions matching the input.
17846              *
17847              * @param {Element} element The element root to search from.
17848              * @param {string} expression The binding expression to match.
17849              * @param {boolean} opt_exactMatch If true, only returns exact matches
17850              *     for the expression. Filters and whitespace are ignored.
17851              */
17852             testability.findBindings = function(element, expression, opt_exactMatch) {
17853               var bindings = element.getElementsByClassName('ng-binding');
17854               var matches = [];
17855               forEach(bindings, function(binding) {
17856                 var dataBinding = angular.element(binding).data('$binding');
17857                 if (dataBinding) {
17858                   forEach(dataBinding, function(bindingName) {
17859                     if (opt_exactMatch) {
17860                       var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17861                       if (matcher.test(bindingName)) {
17862                         matches.push(binding);
17863                       }
17864                     } else {
17865                       if (bindingName.indexOf(expression) != -1) {
17866                         matches.push(binding);
17867                       }
17868                     }
17869                   });
17870                 }
17871               });
17872               return matches;
17873             };
17874
17875             /**
17876              * @name $$testability#findModels
17877              *
17878              * @description
17879              * Returns an array of elements that are two-way found via ng-model to
17880              * expressions matching the input.
17881              *
17882              * @param {Element} element The element root to search from.
17883              * @param {string} expression The model expression to match.
17884              * @param {boolean} opt_exactMatch If true, only returns exact matches
17885              *     for the expression.
17886              */
17887             testability.findModels = function(element, expression, opt_exactMatch) {
17888               var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17889               for (var p = 0; p < prefixes.length; ++p) {
17890                 var attributeEquals = opt_exactMatch ? '=' : '*=';
17891                 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17892                 var elements = element.querySelectorAll(selector);
17893                 if (elements.length) {
17894                   return elements;
17895                 }
17896               }
17897             };
17898
17899             /**
17900              * @name $$testability#getLocation
17901              *
17902              * @description
17903              * Shortcut for getting the location in a browser agnostic way. Returns
17904              *     the path, search, and hash. (e.g. /path?a=b#hash)
17905              */
17906             testability.getLocation = function() {
17907               return $location.url();
17908             };
17909
17910             /**
17911              * @name $$testability#setLocation
17912              *
17913              * @description
17914              * Shortcut for navigating to a location without doing a full page reload.
17915              *
17916              * @param {string} url The location url (path, search and hash,
17917              *     e.g. /path?a=b#hash) to go to.
17918              */
17919             testability.setLocation = function(url) {
17920               if (url !== $location.url()) {
17921                 $location.url(url);
17922                 $rootScope.$digest();
17923               }
17924             };
17925
17926             /**
17927              * @name $$testability#whenStable
17928              *
17929              * @description
17930              * Calls the callback when $timeout and $http requests are completed.
17931              *
17932              * @param {function} callback
17933              */
17934             testability.whenStable = function(callback) {
17935               $browser.notifyWhenNoOutstandingRequests(callback);
17936             };
17937
17938             return testability;
17939           }];
17940         }
17941
17942         function $TimeoutProvider() {
17943           this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17944                function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17945
17946             var deferreds = {};
17947
17948
17949              /**
17950               * @ngdoc service
17951               * @name $timeout
17952               *
17953               * @description
17954               * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17955               * block and delegates any exceptions to
17956               * {@link ng.$exceptionHandler $exceptionHandler} service.
17957               *
17958               * The return value of calling `$timeout` is a promise, which will be resolved when
17959               * the delay has passed and the timeout function, if provided, is executed.
17960               *
17961               * To cancel a timeout request, call `$timeout.cancel(promise)`.
17962               *
17963               * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17964               * synchronously flush the queue of deferred functions.
17965               *
17966               * If you only want a promise that will be resolved after some specified delay
17967               * then you can call `$timeout` without the `fn` function.
17968               *
17969               * @param {function()=} fn A function, whose execution should be delayed.
17970               * @param {number=} [delay=0] Delay in milliseconds.
17971               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17972               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17973               * @param {...*=} Pass additional parameters to the executed function.
17974               * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17975               *   promise will be resolved with is the return value of the `fn` function.
17976               *
17977               */
17978             function timeout(fn, delay, invokeApply) {
17979               if (!isFunction(fn)) {
17980                 invokeApply = delay;
17981                 delay = fn;
17982                 fn = noop;
17983               }
17984
17985               var args = sliceArgs(arguments, 3),
17986                   skipApply = (isDefined(invokeApply) && !invokeApply),
17987                   deferred = (skipApply ? $$q : $q).defer(),
17988                   promise = deferred.promise,
17989                   timeoutId;
17990
17991               timeoutId = $browser.defer(function() {
17992                 try {
17993                   deferred.resolve(fn.apply(null, args));
17994                 } catch (e) {
17995                   deferred.reject(e);
17996                   $exceptionHandler(e);
17997                 }
17998                 finally {
17999                   delete deferreds[promise.$$timeoutId];
18000                 }
18001
18002                 if (!skipApply) $rootScope.$apply();
18003               }, delay);
18004
18005               promise.$$timeoutId = timeoutId;
18006               deferreds[timeoutId] = deferred;
18007
18008               return promise;
18009             }
18010
18011
18012              /**
18013               * @ngdoc method
18014               * @name $timeout#cancel
18015               *
18016               * @description
18017               * Cancels a task associated with the `promise`. As a result of this, the promise will be
18018               * resolved with a rejection.
18019               *
18020               * @param {Promise=} promise Promise returned by the `$timeout` function.
18021               * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18022               *   canceled.
18023               */
18024             timeout.cancel = function(promise) {
18025               if (promise && promise.$$timeoutId in deferreds) {
18026                 deferreds[promise.$$timeoutId].reject('canceled');
18027                 delete deferreds[promise.$$timeoutId];
18028                 return $browser.defer.cancel(promise.$$timeoutId);
18029               }
18030               return false;
18031             };
18032
18033             return timeout;
18034           }];
18035         }
18036
18037         // NOTE:  The usage of window and document instead of $window and $document here is
18038         // deliberate.  This service depends on the specific behavior of anchor nodes created by the
18039         // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18040         // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18041         // doesn't know about mocked locations and resolves URLs to the real document - which is
18042         // exactly the behavior needed here.  There is little value is mocking these out for this
18043         // service.
18044         var urlParsingNode = document.createElement("a");
18045         var originUrl = urlResolve(window.location.href);
18046
18047
18048         /**
18049          *
18050          * Implementation Notes for non-IE browsers
18051          * ----------------------------------------
18052          * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18053          * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18054          * URL will be resolved into an absolute URL in the context of the application document.
18055          * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18056          * properties are all populated to reflect the normalized URL.  This approach has wide
18057          * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18058          * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18059          *
18060          * Implementation Notes for IE
18061          * ---------------------------
18062          * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18063          * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18064          * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18065          * work around that by performing the parsing in a 2nd step by taking a previously normalized
18066          * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18067          * properties such as protocol, hostname, port, etc.
18068          *
18069          * References:
18070          *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18071          *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18072          *   http://url.spec.whatwg.org/#urlutils
18073          *   https://github.com/angular/angular.js/pull/2902
18074          *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18075          *
18076          * @kind function
18077          * @param {string} url The URL to be parsed.
18078          * @description Normalizes and parses a URL.
18079          * @returns {object} Returns the normalized URL as a dictionary.
18080          *
18081          *   | member name   | Description    |
18082          *   |---------------|----------------|
18083          *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18084          *   | protocol      | The protocol including the trailing colon                              |
18085          *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18086          *   | search        | The search params, minus the question mark                             |
18087          *   | hash          | The hash string, minus the hash symbol
18088          *   | hostname      | The hostname
18089          *   | port          | The port, without ":"
18090          *   | pathname      | The pathname, beginning with "/"
18091          *
18092          */
18093         function urlResolve(url) {
18094           var href = url;
18095
18096           if (msie) {
18097             // Normalize before parse.  Refer Implementation Notes on why this is
18098             // done in two steps on IE.
18099             urlParsingNode.setAttribute("href", href);
18100             href = urlParsingNode.href;
18101           }
18102
18103           urlParsingNode.setAttribute('href', href);
18104
18105           // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18106           return {
18107             href: urlParsingNode.href,
18108             protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18109             host: urlParsingNode.host,
18110             search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18111             hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18112             hostname: urlParsingNode.hostname,
18113             port: urlParsingNode.port,
18114             pathname: (urlParsingNode.pathname.charAt(0) === '/')
18115               ? urlParsingNode.pathname
18116               : '/' + urlParsingNode.pathname
18117           };
18118         }
18119
18120         /**
18121          * Parse a request URL and determine whether this is a same-origin request as the application document.
18122          *
18123          * @param {string|object} requestUrl The url of the request as a string that will be resolved
18124          * or a parsed URL object.
18125          * @returns {boolean} Whether the request is for the same origin as the application document.
18126          */
18127         function urlIsSameOrigin(requestUrl) {
18128           var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18129           return (parsed.protocol === originUrl.protocol &&
18130                   parsed.host === originUrl.host);
18131         }
18132
18133         /**
18134          * @ngdoc service
18135          * @name $window
18136          *
18137          * @description
18138          * A reference to the browser's `window` object. While `window`
18139          * is globally available in JavaScript, it causes testability problems, because
18140          * it is a global variable. In angular we always refer to it through the
18141          * `$window` service, so it may be overridden, removed or mocked for testing.
18142          *
18143          * Expressions, like the one defined for the `ngClick` directive in the example
18144          * below, are evaluated with respect to the current scope.  Therefore, there is
18145          * no risk of inadvertently coding in a dependency on a global value in such an
18146          * expression.
18147          *
18148          * @example
18149            <example module="windowExample">
18150              <file name="index.html">
18151                <script>
18152                  angular.module('windowExample', [])
18153                    .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18154                      $scope.greeting = 'Hello, World!';
18155                      $scope.doGreeting = function(greeting) {
18156                        $window.alert(greeting);
18157                      };
18158                    }]);
18159                </script>
18160                <div ng-controller="ExampleController">
18161                  <input type="text" ng-model="greeting" aria-label="greeting" />
18162                  <button ng-click="doGreeting(greeting)">ALERT</button>
18163                </div>
18164              </file>
18165              <file name="protractor.js" type="protractor">
18166               it('should display the greeting in the input box', function() {
18167                element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18168                // If we click the button it will block the test runner
18169                // element(':button').click();
18170               });
18171              </file>
18172            </example>
18173          */
18174         function $WindowProvider() {
18175           this.$get = valueFn(window);
18176         }
18177
18178         /**
18179          * @name $$cookieReader
18180          * @requires $document
18181          *
18182          * @description
18183          * This is a private service for reading cookies used by $http and ngCookies
18184          *
18185          * @return {Object} a key/value map of the current cookies
18186          */
18187         function $$CookieReader($document) {
18188           var rawDocument = $document[0] || {};
18189           var lastCookies = {};
18190           var lastCookieString = '';
18191
18192           function safeDecodeURIComponent(str) {
18193             try {
18194               return decodeURIComponent(str);
18195             } catch (e) {
18196               return str;
18197             }
18198           }
18199
18200           return function() {
18201             var cookieArray, cookie, i, index, name;
18202             var currentCookieString = rawDocument.cookie || '';
18203
18204             if (currentCookieString !== lastCookieString) {
18205               lastCookieString = currentCookieString;
18206               cookieArray = lastCookieString.split('; ');
18207               lastCookies = {};
18208
18209               for (i = 0; i < cookieArray.length; i++) {
18210                 cookie = cookieArray[i];
18211                 index = cookie.indexOf('=');
18212                 if (index > 0) { //ignore nameless cookies
18213                   name = safeDecodeURIComponent(cookie.substring(0, index));
18214                   // the first value that is seen for a cookie is the most
18215                   // specific one.  values for the same cookie name that
18216                   // follow are for less specific paths.
18217                   if (isUndefined(lastCookies[name])) {
18218                     lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18219                   }
18220                 }
18221               }
18222             }
18223             return lastCookies;
18224           };
18225         }
18226
18227         $$CookieReader.$inject = ['$document'];
18228
18229         function $$CookieReaderProvider() {
18230           this.$get = $$CookieReader;
18231         }
18232
18233         /* global currencyFilter: true,
18234          dateFilter: true,
18235          filterFilter: true,
18236          jsonFilter: true,
18237          limitToFilter: true,
18238          lowercaseFilter: true,
18239          numberFilter: true,
18240          orderByFilter: true,
18241          uppercaseFilter: true,
18242          */
18243
18244         /**
18245          * @ngdoc provider
18246          * @name $filterProvider
18247          * @description
18248          *
18249          * Filters are just functions which transform input to an output. However filters need to be
18250          * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18251          * annotated with dependencies and is responsible for creating a filter function.
18252          *
18253          * <div class="alert alert-warning">
18254          * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18255          * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18256          * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18257          * (`myapp_subsection_filterx`).
18258          * </div>
18259          *
18260          * ```js
18261          *   // Filter registration
18262          *   function MyModule($provide, $filterProvider) {
18263          *     // create a service to demonstrate injection (not always needed)
18264          *     $provide.value('greet', function(name){
18265          *       return 'Hello ' + name + '!';
18266          *     });
18267          *
18268          *     // register a filter factory which uses the
18269          *     // greet service to demonstrate DI.
18270          *     $filterProvider.register('greet', function(greet){
18271          *       // return the filter function which uses the greet service
18272          *       // to generate salutation
18273          *       return function(text) {
18274          *         // filters need to be forgiving so check input validity
18275          *         return text && greet(text) || text;
18276          *       };
18277          *     });
18278          *   }
18279          * ```
18280          *
18281          * The filter function is registered with the `$injector` under the filter name suffix with
18282          * `Filter`.
18283          *
18284          * ```js
18285          *   it('should be the same instance', inject(
18286          *     function($filterProvider) {
18287          *       $filterProvider.register('reverse', function(){
18288          *         return ...;
18289          *       });
18290          *     },
18291          *     function($filter, reverseFilter) {
18292          *       expect($filter('reverse')).toBe(reverseFilter);
18293          *     });
18294          * ```
18295          *
18296          *
18297          * For more information about how angular filters work, and how to create your own filters, see
18298          * {@link guide/filter Filters} in the Angular Developer Guide.
18299          */
18300
18301         /**
18302          * @ngdoc service
18303          * @name $filter
18304          * @kind function
18305          * @description
18306          * Filters are used for formatting data displayed to the user.
18307          *
18308          * The general syntax in templates is as follows:
18309          *
18310          *         {{ expression [| filter_name[:parameter_value] ... ] }}
18311          *
18312          * @param {String} name Name of the filter function to retrieve
18313          * @return {Function} the filter function
18314          * @example
18315            <example name="$filter" module="filterExample">
18316              <file name="index.html">
18317                <div ng-controller="MainCtrl">
18318                 <h3>{{ originalText }}</h3>
18319                 <h3>{{ filteredText }}</h3>
18320                </div>
18321              </file>
18322
18323              <file name="script.js">
18324               angular.module('filterExample', [])
18325               .controller('MainCtrl', function($scope, $filter) {
18326                 $scope.originalText = 'hello';
18327                 $scope.filteredText = $filter('uppercase')($scope.originalText);
18328               });
18329              </file>
18330            </example>
18331           */
18332         $FilterProvider.$inject = ['$provide'];
18333         function $FilterProvider($provide) {
18334           var suffix = 'Filter';
18335
18336           /**
18337            * @ngdoc method
18338            * @name $filterProvider#register
18339            * @param {string|Object} name Name of the filter function, or an object map of filters where
18340            *    the keys are the filter names and the values are the filter factories.
18341            *
18342            *    <div class="alert alert-warning">
18343            *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18344            *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18345            *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18346            *    (`myapp_subsection_filterx`).
18347            *    </div>
18348             * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18349            * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18350            *    of the registered filter instances.
18351            */
18352           function register(name, factory) {
18353             if (isObject(name)) {
18354               var filters = {};
18355               forEach(name, function(filter, key) {
18356                 filters[key] = register(key, filter);
18357               });
18358               return filters;
18359             } else {
18360               return $provide.factory(name + suffix, factory);
18361             }
18362           }
18363           this.register = register;
18364
18365           this.$get = ['$injector', function($injector) {
18366             return function(name) {
18367               return $injector.get(name + suffix);
18368             };
18369           }];
18370
18371           ////////////////////////////////////////
18372
18373           /* global
18374             currencyFilter: false,
18375             dateFilter: false,
18376             filterFilter: false,
18377             jsonFilter: false,
18378             limitToFilter: false,
18379             lowercaseFilter: false,
18380             numberFilter: false,
18381             orderByFilter: false,
18382             uppercaseFilter: false,
18383           */
18384
18385           register('currency', currencyFilter);
18386           register('date', dateFilter);
18387           register('filter', filterFilter);
18388           register('json', jsonFilter);
18389           register('limitTo', limitToFilter);
18390           register('lowercase', lowercaseFilter);
18391           register('number', numberFilter);
18392           register('orderBy', orderByFilter);
18393           register('uppercase', uppercaseFilter);
18394         }
18395
18396         /**
18397          * @ngdoc filter
18398          * @name filter
18399          * @kind function
18400          *
18401          * @description
18402          * Selects a subset of items from `array` and returns it as a new array.
18403          *
18404          * @param {Array} array The source array.
18405          * @param {string|Object|function()} expression The predicate to be used for selecting items from
18406          *   `array`.
18407          *
18408          *   Can be one of:
18409          *
18410          *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18411          *     objects with string properties in `array` that match this string will be returned. This also
18412          *     applies to nested object properties.
18413          *     The predicate can be negated by prefixing the string with `!`.
18414          *
18415          *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18416          *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18417          *     which have property `name` containing "M" and property `phone` containing "1". A special
18418          *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18419          *     property of the object or its nested object properties. That's equivalent to the simple
18420          *     substring match with a `string` as described above. The predicate can be negated by prefixing
18421          *     the string with `!`.
18422          *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18423          *     not containing "M".
18424          *
18425          *     Note that a named property will match properties on the same level only, while the special
18426          *     `$` property will match properties on the same level or deeper. E.g. an array item like
18427          *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18428          *     **will** be matched by `{$: 'John'}`.
18429          *
18430          *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18431          *     The function is called for each element of the array, with the element, its index, and
18432          *     the entire array itself as arguments.
18433          *
18434          *     The final result is an array of those elements that the predicate returned true for.
18435          *
18436          * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18437          *     determining if the expected value (from the filter expression) and actual value (from
18438          *     the object in the array) should be considered a match.
18439          *
18440          *   Can be one of:
18441          *
18442          *   - `function(actual, expected)`:
18443          *     The function will be given the object value and the predicate value to compare and
18444          *     should return true if both values should be considered equal.
18445          *
18446          *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18447          *     This is essentially strict comparison of expected and actual.
18448          *
18449          *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18450          *     insensitive way.
18451          *
18452          *     Primitive values are converted to strings. Objects are not compared against primitives,
18453          *     unless they have a custom `toString` method (e.g. `Date` objects).
18454          *
18455          * @example
18456            <example>
18457              <file name="index.html">
18458                <div ng-init="friends = [{name:'John', phone:'555-1276'},
18459                                         {name:'Mary', phone:'800-BIG-MARY'},
18460                                         {name:'Mike', phone:'555-4321'},
18461                                         {name:'Adam', phone:'555-5678'},
18462                                         {name:'Julie', phone:'555-8765'},
18463                                         {name:'Juliette', phone:'555-5678'}]"></div>
18464
18465                <label>Search: <input ng-model="searchText"></label>
18466                <table id="searchTextResults">
18467                  <tr><th>Name</th><th>Phone</th></tr>
18468                  <tr ng-repeat="friend in friends | filter:searchText">
18469                    <td>{{friend.name}}</td>
18470                    <td>{{friend.phone}}</td>
18471                  </tr>
18472                </table>
18473                <hr>
18474                <label>Any: <input ng-model="search.$"></label> <br>
18475                <label>Name only <input ng-model="search.name"></label><br>
18476                <label>Phone only <input ng-model="search.phone"></label><br>
18477                <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18478                <table id="searchObjResults">
18479                  <tr><th>Name</th><th>Phone</th></tr>
18480                  <tr ng-repeat="friendObj in friends | filter:search:strict">
18481                    <td>{{friendObj.name}}</td>
18482                    <td>{{friendObj.phone}}</td>
18483                  </tr>
18484                </table>
18485              </file>
18486              <file name="protractor.js" type="protractor">
18487                var expectFriendNames = function(expectedNames, key) {
18488                  element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18489                    arr.forEach(function(wd, i) {
18490                      expect(wd.getText()).toMatch(expectedNames[i]);
18491                    });
18492                  });
18493                };
18494
18495                it('should search across all fields when filtering with a string', function() {
18496                  var searchText = element(by.model('searchText'));
18497                  searchText.clear();
18498                  searchText.sendKeys('m');
18499                  expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18500
18501                  searchText.clear();
18502                  searchText.sendKeys('76');
18503                  expectFriendNames(['John', 'Julie'], 'friend');
18504                });
18505
18506                it('should search in specific fields when filtering with a predicate object', function() {
18507                  var searchAny = element(by.model('search.$'));
18508                  searchAny.clear();
18509                  searchAny.sendKeys('i');
18510                  expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18511                });
18512                it('should use a equal comparison when comparator is true', function() {
18513                  var searchName = element(by.model('search.name'));
18514                  var strict = element(by.model('strict'));
18515                  searchName.clear();
18516                  searchName.sendKeys('Julie');
18517                  strict.click();
18518                  expectFriendNames(['Julie'], 'friendObj');
18519                });
18520              </file>
18521            </example>
18522          */
18523         function filterFilter() {
18524           return function(array, expression, comparator) {
18525             if (!isArrayLike(array)) {
18526               if (array == null) {
18527                 return array;
18528               } else {
18529                 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18530               }
18531             }
18532
18533             var expressionType = getTypeForFilter(expression);
18534             var predicateFn;
18535             var matchAgainstAnyProp;
18536
18537             switch (expressionType) {
18538               case 'function':
18539                 predicateFn = expression;
18540                 break;
18541               case 'boolean':
18542               case 'null':
18543               case 'number':
18544               case 'string':
18545                 matchAgainstAnyProp = true;
18546                 //jshint -W086
18547               case 'object':
18548                 //jshint +W086
18549                 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18550                 break;
18551               default:
18552                 return array;
18553             }
18554
18555             return Array.prototype.filter.call(array, predicateFn);
18556           };
18557         }
18558
18559         // Helper functions for `filterFilter`
18560         function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18561           var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18562           var predicateFn;
18563
18564           if (comparator === true) {
18565             comparator = equals;
18566           } else if (!isFunction(comparator)) {
18567             comparator = function(actual, expected) {
18568               if (isUndefined(actual)) {
18569                 // No substring matching against `undefined`
18570                 return false;
18571               }
18572               if ((actual === null) || (expected === null)) {
18573                 // No substring matching against `null`; only match against `null`
18574                 return actual === expected;
18575               }
18576               if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18577                 // Should not compare primitives against objects, unless they have custom `toString` method
18578                 return false;
18579               }
18580
18581               actual = lowercase('' + actual);
18582               expected = lowercase('' + expected);
18583               return actual.indexOf(expected) !== -1;
18584             };
18585           }
18586
18587           predicateFn = function(item) {
18588             if (shouldMatchPrimitives && !isObject(item)) {
18589               return deepCompare(item, expression.$, comparator, false);
18590             }
18591             return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18592           };
18593
18594           return predicateFn;
18595         }
18596
18597         function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18598           var actualType = getTypeForFilter(actual);
18599           var expectedType = getTypeForFilter(expected);
18600
18601           if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18602             return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18603           } else if (isArray(actual)) {
18604             // In case `actual` is an array, consider it a match
18605             // if ANY of it's items matches `expected`
18606             return actual.some(function(item) {
18607               return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18608             });
18609           }
18610
18611           switch (actualType) {
18612             case 'object':
18613               var key;
18614               if (matchAgainstAnyProp) {
18615                 for (key in actual) {
18616                   if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18617                     return true;
18618                   }
18619                 }
18620                 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18621               } else if (expectedType === 'object') {
18622                 for (key in expected) {
18623                   var expectedVal = expected[key];
18624                   if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18625                     continue;
18626                   }
18627
18628                   var matchAnyProperty = key === '$';
18629                   var actualVal = matchAnyProperty ? actual : actual[key];
18630                   if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18631                     return false;
18632                   }
18633                 }
18634                 return true;
18635               } else {
18636                 return comparator(actual, expected);
18637               }
18638               break;
18639             case 'function':
18640               return false;
18641             default:
18642               return comparator(actual, expected);
18643           }
18644         }
18645
18646         // Used for easily differentiating between `null` and actual `object`
18647         function getTypeForFilter(val) {
18648           return (val === null) ? 'null' : typeof val;
18649         }
18650
18651         /**
18652          * @ngdoc filter
18653          * @name currency
18654          * @kind function
18655          *
18656          * @description
18657          * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18658          * symbol for current locale is used.
18659          *
18660          * @param {number} amount Input to filter.
18661          * @param {string=} symbol Currency symbol or identifier to be displayed.
18662          * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18663          * @returns {string} Formatted number.
18664          *
18665          *
18666          * @example
18667            <example module="currencyExample">
18668              <file name="index.html">
18669                <script>
18670                  angular.module('currencyExample', [])
18671                    .controller('ExampleController', ['$scope', function($scope) {
18672                      $scope.amount = 1234.56;
18673                    }]);
18674                </script>
18675                <div ng-controller="ExampleController">
18676                  <input type="number" ng-model="amount" aria-label="amount"> <br>
18677                  default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18678                  custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18679                  no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18680                </div>
18681              </file>
18682              <file name="protractor.js" type="protractor">
18683                it('should init with 1234.56', function() {
18684                  expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18685                  expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18686                  expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18687                });
18688                it('should update', function() {
18689                  if (browser.params.browser == 'safari') {
18690                    // Safari does not understand the minus key. See
18691                    // https://github.com/angular/protractor/issues/481
18692                    return;
18693                  }
18694                  element(by.model('amount')).clear();
18695                  element(by.model('amount')).sendKeys('-1234');
18696                  expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18697                  expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18698                  expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18699                });
18700              </file>
18701            </example>
18702          */
18703         currencyFilter.$inject = ['$locale'];
18704         function currencyFilter($locale) {
18705           var formats = $locale.NUMBER_FORMATS;
18706           return function(amount, currencySymbol, fractionSize) {
18707             if (isUndefined(currencySymbol)) {
18708               currencySymbol = formats.CURRENCY_SYM;
18709             }
18710
18711             if (isUndefined(fractionSize)) {
18712               fractionSize = formats.PATTERNS[1].maxFrac;
18713             }
18714
18715             // if null or undefined pass it through
18716             return (amount == null)
18717                 ? amount
18718                 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18719                     replace(/\u00A4/g, currencySymbol);
18720           };
18721         }
18722
18723         /**
18724          * @ngdoc filter
18725          * @name number
18726          * @kind function
18727          *
18728          * @description
18729          * Formats a number as text.
18730          *
18731          * If the input is null or undefined, it will just be returned.
18732          * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18733          * If the input is not a number an empty string is returned.
18734          *
18735          *
18736          * @param {number|string} number Number to format.
18737          * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18738          * If this is not provided then the fraction size is computed from the current locale's number
18739          * formatting pattern. In the case of the default locale, it will be 3.
18740          * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18741          *
18742          * @example
18743            <example module="numberFilterExample">
18744              <file name="index.html">
18745                <script>
18746                  angular.module('numberFilterExample', [])
18747                    .controller('ExampleController', ['$scope', function($scope) {
18748                      $scope.val = 1234.56789;
18749                    }]);
18750                </script>
18751                <div ng-controller="ExampleController">
18752                  <label>Enter number: <input ng-model='val'></label><br>
18753                  Default formatting: <span id='number-default'>{{val | number}}</span><br>
18754                  No fractions: <span>{{val | number:0}}</span><br>
18755                  Negative number: <span>{{-val | number:4}}</span>
18756                </div>
18757              </file>
18758              <file name="protractor.js" type="protractor">
18759                it('should format numbers', function() {
18760                  expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18761                  expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18762                  expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18763                });
18764
18765                it('should update', function() {
18766                  element(by.model('val')).clear();
18767                  element(by.model('val')).sendKeys('3374.333');
18768                  expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18769                  expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18770                  expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18771               });
18772              </file>
18773            </example>
18774          */
18775
18776
18777         numberFilter.$inject = ['$locale'];
18778         function numberFilter($locale) {
18779           var formats = $locale.NUMBER_FORMATS;
18780           return function(number, fractionSize) {
18781
18782             // if null or undefined pass it through
18783             return (number == null)
18784                 ? number
18785                 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18786                                fractionSize);
18787           };
18788         }
18789
18790         var DECIMAL_SEP = '.';
18791         function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18792           if (isObject(number)) return '';
18793
18794           var isNegative = number < 0;
18795           number = Math.abs(number);
18796
18797           var isInfinity = number === Infinity;
18798           if (!isInfinity && !isFinite(number)) return '';
18799
18800           var numStr = number + '',
18801               formatedText = '',
18802               hasExponent = false,
18803               parts = [];
18804
18805           if (isInfinity) formatedText = '\u221e';
18806
18807           if (!isInfinity && numStr.indexOf('e') !== -1) {
18808             var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18809             if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18810               number = 0;
18811             } else {
18812               formatedText = numStr;
18813               hasExponent = true;
18814             }
18815           }
18816
18817           if (!isInfinity && !hasExponent) {
18818             var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18819
18820             // determine fractionSize if it is not specified
18821             if (isUndefined(fractionSize)) {
18822               fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18823             }
18824
18825             // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18826             // inspired by:
18827             // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18828             number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18829
18830             var fraction = ('' + number).split(DECIMAL_SEP);
18831             var whole = fraction[0];
18832             fraction = fraction[1] || '';
18833
18834             var i, pos = 0,
18835                 lgroup = pattern.lgSize,
18836                 group = pattern.gSize;
18837
18838             if (whole.length >= (lgroup + group)) {
18839               pos = whole.length - lgroup;
18840               for (i = 0; i < pos; i++) {
18841                 if ((pos - i) % group === 0 && i !== 0) {
18842                   formatedText += groupSep;
18843                 }
18844                 formatedText += whole.charAt(i);
18845               }
18846             }
18847
18848             for (i = pos; i < whole.length; i++) {
18849               if ((whole.length - i) % lgroup === 0 && i !== 0) {
18850                 formatedText += groupSep;
18851               }
18852               formatedText += whole.charAt(i);
18853             }
18854
18855             // format fraction part.
18856             while (fraction.length < fractionSize) {
18857               fraction += '0';
18858             }
18859
18860             if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18861           } else {
18862             if (fractionSize > 0 && number < 1) {
18863               formatedText = number.toFixed(fractionSize);
18864               number = parseFloat(formatedText);
18865               formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18866             }
18867           }
18868
18869           if (number === 0) {
18870             isNegative = false;
18871           }
18872
18873           parts.push(isNegative ? pattern.negPre : pattern.posPre,
18874                      formatedText,
18875                      isNegative ? pattern.negSuf : pattern.posSuf);
18876           return parts.join('');
18877         }
18878
18879         function padNumber(num, digits, trim) {
18880           var neg = '';
18881           if (num < 0) {
18882             neg =  '-';
18883             num = -num;
18884           }
18885           num = '' + num;
18886           while (num.length < digits) num = '0' + num;
18887           if (trim) {
18888             num = num.substr(num.length - digits);
18889           }
18890           return neg + num;
18891         }
18892
18893
18894         function dateGetter(name, size, offset, trim) {
18895           offset = offset || 0;
18896           return function(date) {
18897             var value = date['get' + name]();
18898             if (offset > 0 || value > -offset) {
18899               value += offset;
18900             }
18901             if (value === 0 && offset == -12) value = 12;
18902             return padNumber(value, size, trim);
18903           };
18904         }
18905
18906         function dateStrGetter(name, shortForm) {
18907           return function(date, formats) {
18908             var value = date['get' + name]();
18909             var get = uppercase(shortForm ? ('SHORT' + name) : name);
18910
18911             return formats[get][value];
18912           };
18913         }
18914
18915         function timeZoneGetter(date, formats, offset) {
18916           var zone = -1 * offset;
18917           var paddedZone = (zone >= 0) ? "+" : "";
18918
18919           paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18920                         padNumber(Math.abs(zone % 60), 2);
18921
18922           return paddedZone;
18923         }
18924
18925         function getFirstThursdayOfYear(year) {
18926             // 0 = index of January
18927             var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18928             // 4 = index of Thursday (+1 to account for 1st = 5)
18929             // 11 = index of *next* Thursday (+1 account for 1st = 12)
18930             return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18931         }
18932
18933         function getThursdayThisWeek(datetime) {
18934             return new Date(datetime.getFullYear(), datetime.getMonth(),
18935               // 4 = index of Thursday
18936               datetime.getDate() + (4 - datetime.getDay()));
18937         }
18938
18939         function weekGetter(size) {
18940            return function(date) {
18941               var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18942                  thisThurs = getThursdayThisWeek(date);
18943
18944               var diff = +thisThurs - +firstThurs,
18945                  result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18946
18947               return padNumber(result, size);
18948            };
18949         }
18950
18951         function ampmGetter(date, formats) {
18952           return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18953         }
18954
18955         function eraGetter(date, formats) {
18956           return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18957         }
18958
18959         function longEraGetter(date, formats) {
18960           return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18961         }
18962
18963         var DATE_FORMATS = {
18964           yyyy: dateGetter('FullYear', 4),
18965             yy: dateGetter('FullYear', 2, 0, true),
18966              y: dateGetter('FullYear', 1),
18967           MMMM: dateStrGetter('Month'),
18968            MMM: dateStrGetter('Month', true),
18969             MM: dateGetter('Month', 2, 1),
18970              M: dateGetter('Month', 1, 1),
18971             dd: dateGetter('Date', 2),
18972              d: dateGetter('Date', 1),
18973             HH: dateGetter('Hours', 2),
18974              H: dateGetter('Hours', 1),
18975             hh: dateGetter('Hours', 2, -12),
18976              h: dateGetter('Hours', 1, -12),
18977             mm: dateGetter('Minutes', 2),
18978              m: dateGetter('Minutes', 1),
18979             ss: dateGetter('Seconds', 2),
18980              s: dateGetter('Seconds', 1),
18981              // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18982              // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18983            sss: dateGetter('Milliseconds', 3),
18984           EEEE: dateStrGetter('Day'),
18985            EEE: dateStrGetter('Day', true),
18986              a: ampmGetter,
18987              Z: timeZoneGetter,
18988             ww: weekGetter(2),
18989              w: weekGetter(1),
18990              G: eraGetter,
18991              GG: eraGetter,
18992              GGG: eraGetter,
18993              GGGG: longEraGetter
18994         };
18995
18996         var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18997             NUMBER_STRING = /^\-?\d+$/;
18998
18999         /**
19000          * @ngdoc filter
19001          * @name date
19002          * @kind function
19003          *
19004          * @description
19005          *   Formats `date` to a string based on the requested `format`.
19006          *
19007          *   `format` string can be composed of the following elements:
19008          *
19009          *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19010          *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19011          *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19012          *   * `'MMMM'`: Month in year (January-December)
19013          *   * `'MMM'`: Month in year (Jan-Dec)
19014          *   * `'MM'`: Month in year, padded (01-12)
19015          *   * `'M'`: Month in year (1-12)
19016          *   * `'dd'`: Day in month, padded (01-31)
19017          *   * `'d'`: Day in month (1-31)
19018          *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19019          *   * `'EEE'`: Day in Week, (Sun-Sat)
19020          *   * `'HH'`: Hour in day, padded (00-23)
19021          *   * `'H'`: Hour in day (0-23)
19022          *   * `'hh'`: Hour in AM/PM, padded (01-12)
19023          *   * `'h'`: Hour in AM/PM, (1-12)
19024          *   * `'mm'`: Minute in hour, padded (00-59)
19025          *   * `'m'`: Minute in hour (0-59)
19026          *   * `'ss'`: Second in minute, padded (00-59)
19027          *   * `'s'`: Second in minute (0-59)
19028          *   * `'sss'`: Millisecond in second, padded (000-999)
19029          *   * `'a'`: AM/PM marker
19030          *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19031          *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19032          *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19033          *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19034          *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19035          *
19036          *   `format` string can also be one of the following predefined
19037          *   {@link guide/i18n localizable formats}:
19038          *
19039          *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19040          *     (e.g. Sep 3, 2010 12:05:08 PM)
19041          *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19042          *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19043          *     (e.g. Friday, September 3, 2010)
19044          *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19045          *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19046          *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19047          *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19048          *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19049          *
19050          *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19051          *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19052          *   (e.g. `"h 'o''clock'"`).
19053          *
19054          * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19055          *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19056          *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19057          *    specified in the string input, the time is considered to be in the local timezone.
19058          * @param {string=} format Formatting rules (see Description). If not specified,
19059          *    `mediumDate` is used.
19060          * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19061          *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19062          *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19063          *    If not specified, the timezone of the browser will be used.
19064          * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19065          *
19066          * @example
19067            <example>
19068              <file name="index.html">
19069                <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19070                    <span>{{1288323623006 | date:'medium'}}</span><br>
19071                <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19072                   <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19073                <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19074                   <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19075                <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19076                   <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19077              </file>
19078              <file name="protractor.js" type="protractor">
19079                it('should format date', function() {
19080                  expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19081                     toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19082                  expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19083                     toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19084                  expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19085                     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19086                  expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19087                     toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19088                });
19089              </file>
19090            </example>
19091          */
19092         dateFilter.$inject = ['$locale'];
19093         function dateFilter($locale) {
19094
19095
19096           var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19097                              // 1        2       3         4          5          6          7          8  9     10      11
19098           function jsonStringToDate(string) {
19099             var match;
19100             if (match = string.match(R_ISO8601_STR)) {
19101               var date = new Date(0),
19102                   tzHour = 0,
19103                   tzMin  = 0,
19104                   dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19105                   timeSetter = match[8] ? date.setUTCHours : date.setHours;
19106
19107               if (match[9]) {
19108                 tzHour = toInt(match[9] + match[10]);
19109                 tzMin = toInt(match[9] + match[11]);
19110               }
19111               dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19112               var h = toInt(match[4] || 0) - tzHour;
19113               var m = toInt(match[5] || 0) - tzMin;
19114               var s = toInt(match[6] || 0);
19115               var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19116               timeSetter.call(date, h, m, s, ms);
19117               return date;
19118             }
19119             return string;
19120           }
19121
19122
19123           return function(date, format, timezone) {
19124             var text = '',
19125                 parts = [],
19126                 fn, match;
19127
19128             format = format || 'mediumDate';
19129             format = $locale.DATETIME_FORMATS[format] || format;
19130             if (isString(date)) {
19131               date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19132             }
19133
19134             if (isNumber(date)) {
19135               date = new Date(date);
19136             }
19137
19138             if (!isDate(date) || !isFinite(date.getTime())) {
19139               return date;
19140             }
19141
19142             while (format) {
19143               match = DATE_FORMATS_SPLIT.exec(format);
19144               if (match) {
19145                 parts = concat(parts, match, 1);
19146                 format = parts.pop();
19147               } else {
19148                 parts.push(format);
19149                 format = null;
19150               }
19151             }
19152
19153             var dateTimezoneOffset = date.getTimezoneOffset();
19154             if (timezone) {
19155               dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19156               date = convertTimezoneToLocal(date, timezone, true);
19157             }
19158             forEach(parts, function(value) {
19159               fn = DATE_FORMATS[value];
19160               text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19161                          : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19162             });
19163
19164             return text;
19165           };
19166         }
19167
19168
19169         /**
19170          * @ngdoc filter
19171          * @name json
19172          * @kind function
19173          *
19174          * @description
19175          *   Allows you to convert a JavaScript object into JSON string.
19176          *
19177          *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19178          *   the binding is automatically converted to JSON.
19179          *
19180          * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19181          * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19182          * @returns {string} JSON string.
19183          *
19184          *
19185          * @example
19186            <example>
19187              <file name="index.html">
19188                <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19189                <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19190              </file>
19191              <file name="protractor.js" type="protractor">
19192                it('should jsonify filtered objects', function() {
19193                  expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19194                  expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19195                });
19196              </file>
19197            </example>
19198          *
19199          */
19200         function jsonFilter() {
19201           return function(object, spacing) {
19202             if (isUndefined(spacing)) {
19203                 spacing = 2;
19204             }
19205             return toJson(object, spacing);
19206           };
19207         }
19208
19209
19210         /**
19211          * @ngdoc filter
19212          * @name lowercase
19213          * @kind function
19214          * @description
19215          * Converts string to lowercase.
19216          * @see angular.lowercase
19217          */
19218         var lowercaseFilter = valueFn(lowercase);
19219
19220
19221         /**
19222          * @ngdoc filter
19223          * @name uppercase
19224          * @kind function
19225          * @description
19226          * Converts string to uppercase.
19227          * @see angular.uppercase
19228          */
19229         var uppercaseFilter = valueFn(uppercase);
19230
19231         /**
19232          * @ngdoc filter
19233          * @name limitTo
19234          * @kind function
19235          *
19236          * @description
19237          * Creates a new array or string containing only a specified number of elements. The elements
19238          * are taken from either the beginning or the end of the source array, string or number, as specified by
19239          * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19240          * converted to a string.
19241          *
19242          * @param {Array|string|number} input Source array, string or number to be limited.
19243          * @param {string|number} limit The length of the returned array or string. If the `limit` number
19244          *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19245          *     If the number is negative, `limit` number  of items from the end of the source array/string
19246          *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19247          *     the input will be returned unchanged.
19248          * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19249          *     indicates an offset from the end of `input`. Defaults to `0`.
19250          * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19251          *     had less than `limit` elements.
19252          *
19253          * @example
19254            <example module="limitToExample">
19255              <file name="index.html">
19256                <script>
19257                  angular.module('limitToExample', [])
19258                    .controller('ExampleController', ['$scope', function($scope) {
19259                      $scope.numbers = [1,2,3,4,5,6,7,8,9];
19260                      $scope.letters = "abcdefghi";
19261                      $scope.longNumber = 2345432342;
19262                      $scope.numLimit = 3;
19263                      $scope.letterLimit = 3;
19264                      $scope.longNumberLimit = 3;
19265                    }]);
19266                </script>
19267                <div ng-controller="ExampleController">
19268                  <label>
19269                     Limit {{numbers}} to:
19270                     <input type="number" step="1" ng-model="numLimit">
19271                  </label>
19272                  <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19273                  <label>
19274                     Limit {{letters}} to:
19275                     <input type="number" step="1" ng-model="letterLimit">
19276                  </label>
19277                  <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19278                  <label>
19279                     Limit {{longNumber}} to:
19280                     <input type="number" step="1" ng-model="longNumberLimit">
19281                  </label>
19282                  <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19283                </div>
19284              </file>
19285              <file name="protractor.js" type="protractor">
19286                var numLimitInput = element(by.model('numLimit'));
19287                var letterLimitInput = element(by.model('letterLimit'));
19288                var longNumberLimitInput = element(by.model('longNumberLimit'));
19289                var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19290                var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19291                var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19292
19293                it('should limit the number array to first three items', function() {
19294                  expect(numLimitInput.getAttribute('value')).toBe('3');
19295                  expect(letterLimitInput.getAttribute('value')).toBe('3');
19296                  expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19297                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19298                  expect(limitedLetters.getText()).toEqual('Output letters: abc');
19299                  expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19300                });
19301
19302                // There is a bug in safari and protractor that doesn't like the minus key
19303                // it('should update the output when -3 is entered', function() {
19304                //   numLimitInput.clear();
19305                //   numLimitInput.sendKeys('-3');
19306                //   letterLimitInput.clear();
19307                //   letterLimitInput.sendKeys('-3');
19308                //   longNumberLimitInput.clear();
19309                //   longNumberLimitInput.sendKeys('-3');
19310                //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19311                //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19312                //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19313                // });
19314
19315                it('should not exceed the maximum size of input array', function() {
19316                  numLimitInput.clear();
19317                  numLimitInput.sendKeys('100');
19318                  letterLimitInput.clear();
19319                  letterLimitInput.sendKeys('100');
19320                  longNumberLimitInput.clear();
19321                  longNumberLimitInput.sendKeys('100');
19322                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19323                  expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19324                  expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19325                });
19326              </file>
19327            </example>
19328         */
19329         function limitToFilter() {
19330           return function(input, limit, begin) {
19331             if (Math.abs(Number(limit)) === Infinity) {
19332               limit = Number(limit);
19333             } else {
19334               limit = toInt(limit);
19335             }
19336             if (isNaN(limit)) return input;
19337
19338             if (isNumber(input)) input = input.toString();
19339             if (!isArray(input) && !isString(input)) return input;
19340
19341             begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19342             begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19343
19344             if (limit >= 0) {
19345               return input.slice(begin, begin + limit);
19346             } else {
19347               if (begin === 0) {
19348                 return input.slice(limit, input.length);
19349               } else {
19350                 return input.slice(Math.max(0, begin + limit), begin);
19351               }
19352             }
19353           };
19354         }
19355
19356         /**
19357          * @ngdoc filter
19358          * @name orderBy
19359          * @kind function
19360          *
19361          * @description
19362          * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19363          * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19364          * as expected, make sure they are actually being saved as numbers and not strings.
19365          *
19366          * @param {Array} array The array to sort.
19367          * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19368          *    used by the comparator to determine the order of elements.
19369          *
19370          *    Can be one of:
19371          *
19372          *    - `function`: Getter function. The result of this function will be sorted using the
19373          *      `<`, `===`, `>` operator.
19374          *    - `string`: An Angular expression. The result of this expression is used to compare elements
19375          *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19376          *      3 first characters of a property called `name`). The result of a constant expression
19377          *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19378          *      to sort object by the value of their `special name` property). An expression can be
19379          *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19380          *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19381          *      element itself is used to compare where sorting.
19382          *    - `Array`: An array of function or string predicates. The first predicate in the array
19383          *      is used for sorting, but when two items are equivalent, the next predicate is used.
19384          *
19385          *    If the predicate is missing or empty then it defaults to `'+'`.
19386          *
19387          * @param {boolean=} reverse Reverse the order of the array.
19388          * @returns {Array} Sorted copy of the source array.
19389          *
19390          *
19391          * @example
19392          * The example below demonstrates a simple ngRepeat, where the data is sorted
19393          * by age in descending order (predicate is set to `'-age'`).
19394          * `reverse` is not set, which means it defaults to `false`.
19395            <example module="orderByExample">
19396              <file name="index.html">
19397                <script>
19398                  angular.module('orderByExample', [])
19399                    .controller('ExampleController', ['$scope', function($scope) {
19400                      $scope.friends =
19401                          [{name:'John', phone:'555-1212', age:10},
19402                           {name:'Mary', phone:'555-9876', age:19},
19403                           {name:'Mike', phone:'555-4321', age:21},
19404                           {name:'Adam', phone:'555-5678', age:35},
19405                           {name:'Julie', phone:'555-8765', age:29}];
19406                    }]);
19407                </script>
19408                <div ng-controller="ExampleController">
19409                  <table class="friend">
19410                    <tr>
19411                      <th>Name</th>
19412                      <th>Phone Number</th>
19413                      <th>Age</th>
19414                    </tr>
19415                    <tr ng-repeat="friend in friends | orderBy:'-age'">
19416                      <td>{{friend.name}}</td>
19417                      <td>{{friend.phone}}</td>
19418                      <td>{{friend.age}}</td>
19419                    </tr>
19420                  </table>
19421                </div>
19422              </file>
19423            </example>
19424          *
19425          * The predicate and reverse parameters can be controlled dynamically through scope properties,
19426          * as shown in the next example.
19427          * @example
19428            <example module="orderByExample">
19429              <file name="index.html">
19430                <script>
19431                  angular.module('orderByExample', [])
19432                    .controller('ExampleController', ['$scope', function($scope) {
19433                      $scope.friends =
19434                          [{name:'John', phone:'555-1212', age:10},
19435                           {name:'Mary', phone:'555-9876', age:19},
19436                           {name:'Mike', phone:'555-4321', age:21},
19437                           {name:'Adam', phone:'555-5678', age:35},
19438                           {name:'Julie', phone:'555-8765', age:29}];
19439                      $scope.predicate = 'age';
19440                      $scope.reverse = true;
19441                      $scope.order = function(predicate) {
19442                        $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19443                        $scope.predicate = predicate;
19444                      };
19445                    }]);
19446                </script>
19447                <style type="text/css">
19448                  .sortorder:after {
19449                    content: '\25b2';
19450                  }
19451                  .sortorder.reverse:after {
19452                    content: '\25bc';
19453                  }
19454                </style>
19455                <div ng-controller="ExampleController">
19456                  <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19457                  <hr/>
19458                  [ <a href="" ng-click="predicate=''">unsorted</a> ]
19459                  <table class="friend">
19460                    <tr>
19461                      <th>
19462                        <a href="" ng-click="order('name')">Name</a>
19463                        <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19464                      </th>
19465                      <th>
19466                        <a href="" ng-click="order('phone')">Phone Number</a>
19467                        <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19468                      </th>
19469                      <th>
19470                        <a href="" ng-click="order('age')">Age</a>
19471                        <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19472                      </th>
19473                    </tr>
19474                    <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19475                      <td>{{friend.name}}</td>
19476                      <td>{{friend.phone}}</td>
19477                      <td>{{friend.age}}</td>
19478                    </tr>
19479                  </table>
19480                </div>
19481              </file>
19482            </example>
19483          *
19484          * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19485          * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19486          * desired parameters.
19487          *
19488          * Example:
19489          *
19490          * @example
19491           <example module="orderByExample">
19492             <file name="index.html">
19493               <div ng-controller="ExampleController">
19494                 <table class="friend">
19495                   <tr>
19496                     <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19497                       (<a href="" ng-click="order('-name',false)">^</a>)</th>
19498                     <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19499                     <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19500                   </tr>
19501                   <tr ng-repeat="friend in friends">
19502                     <td>{{friend.name}}</td>
19503                     <td>{{friend.phone}}</td>
19504                     <td>{{friend.age}}</td>
19505                   </tr>
19506                 </table>
19507               </div>
19508             </file>
19509
19510             <file name="script.js">
19511               angular.module('orderByExample', [])
19512                 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19513                   var orderBy = $filter('orderBy');
19514                   $scope.friends = [
19515                     { name: 'John',    phone: '555-1212',    age: 10 },
19516                     { name: 'Mary',    phone: '555-9876',    age: 19 },
19517                     { name: 'Mike',    phone: '555-4321',    age: 21 },
19518                     { name: 'Adam',    phone: '555-5678',    age: 35 },
19519                     { name: 'Julie',   phone: '555-8765',    age: 29 }
19520                   ];
19521                   $scope.order = function(predicate, reverse) {
19522                     $scope.friends = orderBy($scope.friends, predicate, reverse);
19523                   };
19524                   $scope.order('-age',false);
19525                 }]);
19526             </file>
19527         </example>
19528          */
19529         orderByFilter.$inject = ['$parse'];
19530         function orderByFilter($parse) {
19531           return function(array, sortPredicate, reverseOrder) {
19532
19533             if (!(isArrayLike(array))) return array;
19534
19535             if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19536             if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19537
19538             var predicates = processPredicates(sortPredicate, reverseOrder);
19539             // Add a predicate at the end that evaluates to the element index. This makes the
19540             // sort stable as it works as a tie-breaker when all the input predicates cannot
19541             // distinguish between two elements.
19542             predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19543
19544             // The next three lines are a version of a Swartzian Transform idiom from Perl
19545             // (sometimes called the Decorate-Sort-Undecorate idiom)
19546             // See https://en.wikipedia.org/wiki/Schwartzian_transform
19547             var compareValues = Array.prototype.map.call(array, getComparisonObject);
19548             compareValues.sort(doComparison);
19549             array = compareValues.map(function(item) { return item.value; });
19550
19551             return array;
19552
19553             function getComparisonObject(value, index) {
19554               return {
19555                 value: value,
19556                 predicateValues: predicates.map(function(predicate) {
19557                   return getPredicateValue(predicate.get(value), index);
19558                 })
19559               };
19560             }
19561
19562             function doComparison(v1, v2) {
19563               var result = 0;
19564               for (var index=0, length = predicates.length; index < length; ++index) {
19565                 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19566                 if (result) break;
19567               }
19568               return result;
19569             }
19570           };
19571
19572           function processPredicates(sortPredicate, reverseOrder) {
19573             reverseOrder = reverseOrder ? -1 : 1;
19574             return sortPredicate.map(function(predicate) {
19575               var descending = 1, get = identity;
19576
19577               if (isFunction(predicate)) {
19578                 get = predicate;
19579               } else if (isString(predicate)) {
19580                 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19581                   descending = predicate.charAt(0) == '-' ? -1 : 1;
19582                   predicate = predicate.substring(1);
19583                 }
19584                 if (predicate !== '') {
19585                   get = $parse(predicate);
19586                   if (get.constant) {
19587                     var key = get();
19588                     get = function(value) { return value[key]; };
19589                   }
19590                 }
19591               }
19592               return { get: get, descending: descending * reverseOrder };
19593             });
19594           }
19595
19596           function isPrimitive(value) {
19597             switch (typeof value) {
19598               case 'number': /* falls through */
19599               case 'boolean': /* falls through */
19600               case 'string':
19601                 return true;
19602               default:
19603                 return false;
19604             }
19605           }
19606
19607           function objectValue(value, index) {
19608             // If `valueOf` is a valid function use that
19609             if (typeof value.valueOf === 'function') {
19610               value = value.valueOf();
19611               if (isPrimitive(value)) return value;
19612             }
19613             // If `toString` is a valid function and not the one from `Object.prototype` use that
19614             if (hasCustomToString(value)) {
19615               value = value.toString();
19616               if (isPrimitive(value)) return value;
19617             }
19618             // We have a basic object so we use the position of the object in the collection
19619             return index;
19620           }
19621
19622           function getPredicateValue(value, index) {
19623             var type = typeof value;
19624             if (value === null) {
19625               type = 'string';
19626               value = 'null';
19627             } else if (type === 'string') {
19628               value = value.toLowerCase();
19629             } else if (type === 'object') {
19630               value = objectValue(value, index);
19631             }
19632             return { value: value, type: type };
19633           }
19634
19635           function compare(v1, v2) {
19636             var result = 0;
19637             if (v1.type === v2.type) {
19638               if (v1.value !== v2.value) {
19639                 result = v1.value < v2.value ? -1 : 1;
19640               }
19641             } else {
19642               result = v1.type < v2.type ? -1 : 1;
19643             }
19644             return result;
19645           }
19646         }
19647
19648         function ngDirective(directive) {
19649           if (isFunction(directive)) {
19650             directive = {
19651               link: directive
19652             };
19653           }
19654           directive.restrict = directive.restrict || 'AC';
19655           return valueFn(directive);
19656         }
19657
19658         /**
19659          * @ngdoc directive
19660          * @name a
19661          * @restrict E
19662          *
19663          * @description
19664          * Modifies the default behavior of the html A tag so that the default action is prevented when
19665          * the href attribute is empty.
19666          *
19667          * This change permits the easy creation of action links with the `ngClick` directive
19668          * without changing the location or causing page reloads, e.g.:
19669          * `<a href="" ng-click="list.addItem()">Add Item</a>`
19670          */
19671         var htmlAnchorDirective = valueFn({
19672           restrict: 'E',
19673           compile: function(element, attr) {
19674             if (!attr.href && !attr.xlinkHref) {
19675               return function(scope, element) {
19676                 // If the linked element is not an anchor tag anymore, do nothing
19677                 if (element[0].nodeName.toLowerCase() !== 'a') return;
19678
19679                 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19680                 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19681                            'xlink:href' : 'href';
19682                 element.on('click', function(event) {
19683                   // if we have no href url, then don't navigate anywhere.
19684                   if (!element.attr(href)) {
19685                     event.preventDefault();
19686                   }
19687                 });
19688               };
19689             }
19690           }
19691         });
19692
19693         /**
19694          * @ngdoc directive
19695          * @name ngHref
19696          * @restrict A
19697          * @priority 99
19698          *
19699          * @description
19700          * Using Angular markup like `{{hash}}` in an href attribute will
19701          * make the link go to the wrong URL if the user clicks it before
19702          * Angular has a chance to replace the `{{hash}}` markup with its
19703          * value. Until Angular replaces the markup the link will be broken
19704          * and will most likely return a 404 error. The `ngHref` directive
19705          * solves this problem.
19706          *
19707          * The wrong way to write it:
19708          * ```html
19709          * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19710          * ```
19711          *
19712          * The correct way to write it:
19713          * ```html
19714          * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19715          * ```
19716          *
19717          * @element A
19718          * @param {template} ngHref any string which can contain `{{}}` markup.
19719          *
19720          * @example
19721          * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19722          * in links and their different behaviors:
19723             <example>
19724               <file name="index.html">
19725                 <input ng-model="value" /><br />
19726                 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19727                 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19728                 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19729                 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19730                 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19731                 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19732               </file>
19733               <file name="protractor.js" type="protractor">
19734                 it('should execute ng-click but not reload when href without value', function() {
19735                   element(by.id('link-1')).click();
19736                   expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19737                   expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19738                 });
19739
19740                 it('should execute ng-click but not reload when href empty string', function() {
19741                   element(by.id('link-2')).click();
19742                   expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19743                   expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19744                 });
19745
19746                 it('should execute ng-click and change url when ng-href specified', function() {
19747                   expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19748
19749                   element(by.id('link-3')).click();
19750
19751                   // At this point, we navigate away from an Angular page, so we need
19752                   // to use browser.driver to get the base webdriver.
19753
19754                   browser.wait(function() {
19755                     return browser.driver.getCurrentUrl().then(function(url) {
19756                       return url.match(/\/123$/);
19757                     });
19758                   }, 5000, 'page should navigate to /123');
19759                 });
19760
19761                 it('should execute ng-click but not reload when href empty string and name specified', function() {
19762                   element(by.id('link-4')).click();
19763                   expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19764                   expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19765                 });
19766
19767                 it('should execute ng-click but not reload when no href but name specified', function() {
19768                   element(by.id('link-5')).click();
19769                   expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19770                   expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19771                 });
19772
19773                 it('should only change url when only ng-href', function() {
19774                   element(by.model('value')).clear();
19775                   element(by.model('value')).sendKeys('6');
19776                   expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19777
19778                   element(by.id('link-6')).click();
19779
19780                   // At this point, we navigate away from an Angular page, so we need
19781                   // to use browser.driver to get the base webdriver.
19782                   browser.wait(function() {
19783                     return browser.driver.getCurrentUrl().then(function(url) {
19784                       return url.match(/\/6$/);
19785                     });
19786                   }, 5000, 'page should navigate to /6');
19787                 });
19788               </file>
19789             </example>
19790          */
19791
19792         /**
19793          * @ngdoc directive
19794          * @name ngSrc
19795          * @restrict A
19796          * @priority 99
19797          *
19798          * @description
19799          * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19800          * work right: The browser will fetch from the URL with the literal
19801          * text `{{hash}}` until Angular replaces the expression inside
19802          * `{{hash}}`. The `ngSrc` directive solves this problem.
19803          *
19804          * The buggy way to write it:
19805          * ```html
19806          * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19807          * ```
19808          *
19809          * The correct way to write it:
19810          * ```html
19811          * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19812          * ```
19813          *
19814          * @element IMG
19815          * @param {template} ngSrc any string which can contain `{{}}` markup.
19816          */
19817
19818         /**
19819          * @ngdoc directive
19820          * @name ngSrcset
19821          * @restrict A
19822          * @priority 99
19823          *
19824          * @description
19825          * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19826          * work right: The browser will fetch from the URL with the literal
19827          * text `{{hash}}` until Angular replaces the expression inside
19828          * `{{hash}}`. The `ngSrcset` directive solves this problem.
19829          *
19830          * The buggy way to write it:
19831          * ```html
19832          * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19833          * ```
19834          *
19835          * The correct way to write it:
19836          * ```html
19837          * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19838          * ```
19839          *
19840          * @element IMG
19841          * @param {template} ngSrcset any string which can contain `{{}}` markup.
19842          */
19843
19844         /**
19845          * @ngdoc directive
19846          * @name ngDisabled
19847          * @restrict A
19848          * @priority 100
19849          *
19850          * @description
19851          *
19852          * This directive sets the `disabled` attribute on the element if the
19853          * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19854          *
19855          * A special directive is necessary because we cannot use interpolation inside the `disabled`
19856          * attribute.  The following example would make the button enabled on Chrome/Firefox
19857          * but not on older IEs:
19858          *
19859          * ```html
19860          * <!-- See below for an example of ng-disabled being used correctly -->
19861          * <div ng-init="isDisabled = false">
19862          *  <button disabled="{{isDisabled}}">Disabled</button>
19863          * </div>
19864          * ```
19865          *
19866          * This is because the HTML specification does not require browsers to preserve the values of
19867          * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19868          * If we put an Angular interpolation expression into such an attribute then the
19869          * binding information would be lost when the browser removes the attribute.
19870          *
19871          * @example
19872             <example>
19873               <file name="index.html">
19874                 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19875                 <button ng-model="button" ng-disabled="checked">Button</button>
19876               </file>
19877               <file name="protractor.js" type="protractor">
19878                 it('should toggle button', function() {
19879                   expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19880                   element(by.model('checked')).click();
19881                   expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19882                 });
19883               </file>
19884             </example>
19885          *
19886          * @element INPUT
19887          * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19888          *     then the `disabled` attribute will be set on the element
19889          */
19890
19891
19892         /**
19893          * @ngdoc directive
19894          * @name ngChecked
19895          * @restrict A
19896          * @priority 100
19897          *
19898          * @description
19899          * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19900          *
19901          * Note that this directive should not be used together with {@link ngModel `ngModel`},
19902          * as this can lead to unexpected behavior.
19903          *
19904          * ### Why do we need `ngChecked`?
19905          *
19906          * The HTML specification does not require browsers to preserve the values of boolean attributes
19907          * such as checked. (Their presence means true and their absence means false.)
19908          * If we put an Angular interpolation expression into such an attribute then the
19909          * binding information would be lost when the browser removes the attribute.
19910          * The `ngChecked` directive solves this problem for the `checked` attribute.
19911          * This complementary directive is not removed by the browser and so provides
19912          * a permanent reliable place to store the binding information.
19913          * @example
19914             <example>
19915               <file name="index.html">
19916                 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19917                 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19918               </file>
19919               <file name="protractor.js" type="protractor">
19920                 it('should check both checkBoxes', function() {
19921                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19922                   element(by.model('master')).click();
19923                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19924                 });
19925               </file>
19926             </example>
19927          *
19928          * @element INPUT
19929          * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19930          *     then the `checked` attribute will be set on the element
19931          */
19932
19933
19934         /**
19935          * @ngdoc directive
19936          * @name ngReadonly
19937          * @restrict A
19938          * @priority 100
19939          *
19940          * @description
19941          * The HTML specification does not require browsers to preserve the values of boolean attributes
19942          * such as readonly. (Their presence means true and their absence means false.)
19943          * If we put an Angular interpolation expression into such an attribute then the
19944          * binding information would be lost when the browser removes the attribute.
19945          * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19946          * This complementary directive is not removed by the browser and so provides
19947          * a permanent reliable place to store the binding information.
19948          * @example
19949             <example>
19950               <file name="index.html">
19951                 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19952                 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19953               </file>
19954               <file name="protractor.js" type="protractor">
19955                 it('should toggle readonly attr', function() {
19956                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19957                   element(by.model('checked')).click();
19958                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19959                 });
19960               </file>
19961             </example>
19962          *
19963          * @element INPUT
19964          * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19965          *     then special attribute "readonly" will be set on the element
19966          */
19967
19968
19969         /**
19970          * @ngdoc directive
19971          * @name ngSelected
19972          * @restrict A
19973          * @priority 100
19974          *
19975          * @description
19976          * The HTML specification does not require browsers to preserve the values of boolean attributes
19977          * such as selected. (Their presence means true and their absence means false.)
19978          * If we put an Angular interpolation expression into such an attribute then the
19979          * binding information would be lost when the browser removes the attribute.
19980          * The `ngSelected` directive solves this problem for the `selected` attribute.
19981          * This complementary directive is not removed by the browser and so provides
19982          * a permanent reliable place to store the binding information.
19983          *
19984          * @example
19985             <example>
19986               <file name="index.html">
19987                 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19988                 <select aria-label="ngSelected demo">
19989                   <option>Hello!</option>
19990                   <option id="greet" ng-selected="selected">Greetings!</option>
19991                 </select>
19992               </file>
19993               <file name="protractor.js" type="protractor">
19994                 it('should select Greetings!', function() {
19995                   expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19996                   element(by.model('selected')).click();
19997                   expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19998                 });
19999               </file>
20000             </example>
20001          *
20002          * @element OPTION
20003          * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20004          *     then special attribute "selected" will be set on the element
20005          */
20006
20007         /**
20008          * @ngdoc directive
20009          * @name ngOpen
20010          * @restrict A
20011          * @priority 100
20012          *
20013          * @description
20014          * The HTML specification does not require browsers to preserve the values of boolean attributes
20015          * such as open. (Their presence means true and their absence means false.)
20016          * If we put an Angular interpolation expression into such an attribute then the
20017          * binding information would be lost when the browser removes the attribute.
20018          * The `ngOpen` directive solves this problem for the `open` attribute.
20019          * This complementary directive is not removed by the browser and so provides
20020          * a permanent reliable place to store the binding information.
20021          * @example
20022              <example>
20023                <file name="index.html">
20024                  <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20025                  <details id="details" ng-open="open">
20026                     <summary>Show/Hide me</summary>
20027                  </details>
20028                </file>
20029                <file name="protractor.js" type="protractor">
20030                  it('should toggle open', function() {
20031                    expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20032                    element(by.model('open')).click();
20033                    expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20034                  });
20035                </file>
20036              </example>
20037          *
20038          * @element DETAILS
20039          * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20040          *     then special attribute "open" will be set on the element
20041          */
20042
20043         var ngAttributeAliasDirectives = {};
20044
20045         // boolean attrs are evaluated
20046         forEach(BOOLEAN_ATTR, function(propName, attrName) {
20047           // binding to multiple is not supported
20048           if (propName == "multiple") return;
20049
20050           function defaultLinkFn(scope, element, attr) {
20051             scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20052               attr.$set(attrName, !!value);
20053             });
20054           }
20055
20056           var normalized = directiveNormalize('ng-' + attrName);
20057           var linkFn = defaultLinkFn;
20058
20059           if (propName === 'checked') {
20060             linkFn = function(scope, element, attr) {
20061               // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20062               if (attr.ngModel !== attr[normalized]) {
20063                 defaultLinkFn(scope, element, attr);
20064               }
20065             };
20066           }
20067
20068           ngAttributeAliasDirectives[normalized] = function() {
20069             return {
20070               restrict: 'A',
20071               priority: 100,
20072               link: linkFn
20073             };
20074           };
20075         });
20076
20077         // aliased input attrs are evaluated
20078         forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20079           ngAttributeAliasDirectives[ngAttr] = function() {
20080             return {
20081               priority: 100,
20082               link: function(scope, element, attr) {
20083                 //special case ngPattern when a literal regular expression value
20084                 //is used as the expression (this way we don't have to watch anything).
20085                 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20086                   var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20087                   if (match) {
20088                     attr.$set("ngPattern", new RegExp(match[1], match[2]));
20089                     return;
20090                   }
20091                 }
20092
20093                 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20094                   attr.$set(ngAttr, value);
20095                 });
20096               }
20097             };
20098           };
20099         });
20100
20101         // ng-src, ng-srcset, ng-href are interpolated
20102         forEach(['src', 'srcset', 'href'], function(attrName) {
20103           var normalized = directiveNormalize('ng-' + attrName);
20104           ngAttributeAliasDirectives[normalized] = function() {
20105             return {
20106               priority: 99, // it needs to run after the attributes are interpolated
20107               link: function(scope, element, attr) {
20108                 var propName = attrName,
20109                     name = attrName;
20110
20111                 if (attrName === 'href' &&
20112                     toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20113                   name = 'xlinkHref';
20114                   attr.$attr[name] = 'xlink:href';
20115                   propName = null;
20116                 }
20117
20118                 attr.$observe(normalized, function(value) {
20119                   if (!value) {
20120                     if (attrName === 'href') {
20121                       attr.$set(name, null);
20122                     }
20123                     return;
20124                   }
20125
20126                   attr.$set(name, value);
20127
20128                   // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20129                   // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20130                   // to set the property as well to achieve the desired effect.
20131                   // we use attr[attrName] value since $set can sanitize the url.
20132                   if (msie && propName) element.prop(propName, attr[name]);
20133                 });
20134               }
20135             };
20136           };
20137         });
20138
20139         /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20140          */
20141         var nullFormCtrl = {
20142           $addControl: noop,
20143           $$renameControl: nullFormRenameControl,
20144           $removeControl: noop,
20145           $setValidity: noop,
20146           $setDirty: noop,
20147           $setPristine: noop,
20148           $setSubmitted: noop
20149         },
20150         SUBMITTED_CLASS = 'ng-submitted';
20151
20152         function nullFormRenameControl(control, name) {
20153           control.$name = name;
20154         }
20155
20156         /**
20157          * @ngdoc type
20158          * @name form.FormController
20159          *
20160          * @property {boolean} $pristine True if user has not interacted with the form yet.
20161          * @property {boolean} $dirty True if user has already interacted with the form.
20162          * @property {boolean} $valid True if all of the containing forms and controls are valid.
20163          * @property {boolean} $invalid True if at least one containing control or form is invalid.
20164          * @property {boolean} $pending True if at least one containing control or form is pending.
20165          * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20166          *
20167          * @property {Object} $error Is an object hash, containing references to controls or
20168          *  forms with failing validators, where:
20169          *
20170          *  - keys are validation tokens (error names),
20171          *  - values are arrays of controls or forms that have a failing validator for given error name.
20172          *
20173          *  Built-in validation tokens:
20174          *
20175          *  - `email`
20176          *  - `max`
20177          *  - `maxlength`
20178          *  - `min`
20179          *  - `minlength`
20180          *  - `number`
20181          *  - `pattern`
20182          *  - `required`
20183          *  - `url`
20184          *  - `date`
20185          *  - `datetimelocal`
20186          *  - `time`
20187          *  - `week`
20188          *  - `month`
20189          *
20190          * @description
20191          * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20192          * such as being valid/invalid or dirty/pristine.
20193          *
20194          * Each {@link ng.directive:form form} directive creates an instance
20195          * of `FormController`.
20196          *
20197          */
20198         //asks for $scope to fool the BC controller module
20199         FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20200         function FormController(element, attrs, $scope, $animate, $interpolate) {
20201           var form = this,
20202               controls = [];
20203
20204           // init state
20205           form.$error = {};
20206           form.$$success = {};
20207           form.$pending = undefined;
20208           form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20209           form.$dirty = false;
20210           form.$pristine = true;
20211           form.$valid = true;
20212           form.$invalid = false;
20213           form.$submitted = false;
20214           form.$$parentForm = nullFormCtrl;
20215
20216           /**
20217            * @ngdoc method
20218            * @name form.FormController#$rollbackViewValue
20219            *
20220            * @description
20221            * Rollback all form controls pending updates to the `$modelValue`.
20222            *
20223            * Updates may be pending by a debounced event or because the input is waiting for a some future
20224            * event defined in `ng-model-options`. This method is typically needed by the reset button of
20225            * a form that uses `ng-model-options` to pend updates.
20226            */
20227           form.$rollbackViewValue = function() {
20228             forEach(controls, function(control) {
20229               control.$rollbackViewValue();
20230             });
20231           };
20232
20233           /**
20234            * @ngdoc method
20235            * @name form.FormController#$commitViewValue
20236            *
20237            * @description
20238            * Commit all form controls pending updates to the `$modelValue`.
20239            *
20240            * Updates may be pending by a debounced event or because the input is waiting for a some future
20241            * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20242            * usually handles calling this in response to input events.
20243            */
20244           form.$commitViewValue = function() {
20245             forEach(controls, function(control) {
20246               control.$commitViewValue();
20247             });
20248           };
20249
20250           /**
20251            * @ngdoc method
20252            * @name form.FormController#$addControl
20253            * @param {object} control control object, either a {@link form.FormController} or an
20254            * {@link ngModel.NgModelController}
20255            *
20256            * @description
20257            * Register a control with the form. Input elements using ngModelController do this automatically
20258            * when they are linked.
20259            *
20260            * Note that the current state of the control will not be reflected on the new parent form. This
20261            * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20262            * state.
20263            *
20264            * However, if the method is used programmatically, for example by adding dynamically created controls,
20265            * or controls that have been previously removed without destroying their corresponding DOM element,
20266            * it's the developers responsiblity to make sure the current state propagates to the parent form.
20267            *
20268            * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20269            * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20270            */
20271           form.$addControl = function(control) {
20272             // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20273             // and not added to the scope.  Now we throw an error.
20274             assertNotHasOwnProperty(control.$name, 'input');
20275             controls.push(control);
20276
20277             if (control.$name) {
20278               form[control.$name] = control;
20279             }
20280
20281             control.$$parentForm = form;
20282           };
20283
20284           // Private API: rename a form control
20285           form.$$renameControl = function(control, newName) {
20286             var oldName = control.$name;
20287
20288             if (form[oldName] === control) {
20289               delete form[oldName];
20290             }
20291             form[newName] = control;
20292             control.$name = newName;
20293           };
20294
20295           /**
20296            * @ngdoc method
20297            * @name form.FormController#$removeControl
20298            * @param {object} control control object, either a {@link form.FormController} or an
20299            * {@link ngModel.NgModelController}
20300            *
20301            * @description
20302            * Deregister a control from the form.
20303            *
20304            * Input elements using ngModelController do this automatically when they are destroyed.
20305            *
20306            * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20307            * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20308            * different from case to case. For example, removing the only `$dirty` control from a form may or
20309            * may not mean that the form is still `$dirty`.
20310            */
20311           form.$removeControl = function(control) {
20312             if (control.$name && form[control.$name] === control) {
20313               delete form[control.$name];
20314             }
20315             forEach(form.$pending, function(value, name) {
20316               form.$setValidity(name, null, control);
20317             });
20318             forEach(form.$error, function(value, name) {
20319               form.$setValidity(name, null, control);
20320             });
20321             forEach(form.$$success, function(value, name) {
20322               form.$setValidity(name, null, control);
20323             });
20324
20325             arrayRemove(controls, control);
20326             control.$$parentForm = nullFormCtrl;
20327           };
20328
20329
20330           /**
20331            * @ngdoc method
20332            * @name form.FormController#$setValidity
20333            *
20334            * @description
20335            * Sets the validity of a form control.
20336            *
20337            * This method will also propagate to parent forms.
20338            */
20339           addSetValidityMethod({
20340             ctrl: this,
20341             $element: element,
20342             set: function(object, property, controller) {
20343               var list = object[property];
20344               if (!list) {
20345                 object[property] = [controller];
20346               } else {
20347                 var index = list.indexOf(controller);
20348                 if (index === -1) {
20349                   list.push(controller);
20350                 }
20351               }
20352             },
20353             unset: function(object, property, controller) {
20354               var list = object[property];
20355               if (!list) {
20356                 return;
20357               }
20358               arrayRemove(list, controller);
20359               if (list.length === 0) {
20360                 delete object[property];
20361               }
20362             },
20363             $animate: $animate
20364           });
20365
20366           /**
20367            * @ngdoc method
20368            * @name form.FormController#$setDirty
20369            *
20370            * @description
20371            * Sets the form to a dirty state.
20372            *
20373            * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20374            * state (ng-dirty class). This method will also propagate to parent forms.
20375            */
20376           form.$setDirty = function() {
20377             $animate.removeClass(element, PRISTINE_CLASS);
20378             $animate.addClass(element, DIRTY_CLASS);
20379             form.$dirty = true;
20380             form.$pristine = false;
20381             form.$$parentForm.$setDirty();
20382           };
20383
20384           /**
20385            * @ngdoc method
20386            * @name form.FormController#$setPristine
20387            *
20388            * @description
20389            * Sets the form to its pristine state.
20390            *
20391            * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20392            * state (ng-pristine class). This method will also propagate to all the controls contained
20393            * in this form.
20394            *
20395            * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20396            * saving or resetting it.
20397            */
20398           form.$setPristine = function() {
20399             $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20400             form.$dirty = false;
20401             form.$pristine = true;
20402             form.$submitted = false;
20403             forEach(controls, function(control) {
20404               control.$setPristine();
20405             });
20406           };
20407
20408           /**
20409            * @ngdoc method
20410            * @name form.FormController#$setUntouched
20411            *
20412            * @description
20413            * Sets the form to its untouched state.
20414            *
20415            * This method can be called to remove the 'ng-touched' class and set the form controls to their
20416            * untouched state (ng-untouched class).
20417            *
20418            * Setting a form controls back to their untouched state is often useful when setting the form
20419            * back to its pristine state.
20420            */
20421           form.$setUntouched = function() {
20422             forEach(controls, function(control) {
20423               control.$setUntouched();
20424             });
20425           };
20426
20427           /**
20428            * @ngdoc method
20429            * @name form.FormController#$setSubmitted
20430            *
20431            * @description
20432            * Sets the form to its submitted state.
20433            */
20434           form.$setSubmitted = function() {
20435             $animate.addClass(element, SUBMITTED_CLASS);
20436             form.$submitted = true;
20437             form.$$parentForm.$setSubmitted();
20438           };
20439         }
20440
20441         /**
20442          * @ngdoc directive
20443          * @name ngForm
20444          * @restrict EAC
20445          *
20446          * @description
20447          * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20448          * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20449          * sub-group of controls needs to be determined.
20450          *
20451          * Note: the purpose of `ngForm` is to group controls,
20452          * but not to be a replacement for the `<form>` tag with all of its capabilities
20453          * (e.g. posting to the server, ...).
20454          *
20455          * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20456          *                       related scope, under this name.
20457          *
20458          */
20459
20460          /**
20461          * @ngdoc directive
20462          * @name form
20463          * @restrict E
20464          *
20465          * @description
20466          * Directive that instantiates
20467          * {@link form.FormController FormController}.
20468          *
20469          * If the `name` attribute is specified, the form controller is published onto the current scope under
20470          * this name.
20471          *
20472          * # Alias: {@link ng.directive:ngForm `ngForm`}
20473          *
20474          * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20475          * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20476          * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20477          * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20478          * using Angular validation directives in forms that are dynamically generated using the
20479          * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20480          * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20481          * `ngForm` directive and nest these in an outer `form` element.
20482          *
20483          *
20484          * # CSS classes
20485          *  - `ng-valid` is set if the form is valid.
20486          *  - `ng-invalid` is set if the form is invalid.
20487          *  - `ng-pending` is set if the form is pending.
20488          *  - `ng-pristine` is set if the form is pristine.
20489          *  - `ng-dirty` is set if the form is dirty.
20490          *  - `ng-submitted` is set if the form was submitted.
20491          *
20492          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20493          *
20494          *
20495          * # Submitting a form and preventing the default action
20496          *
20497          * Since the role of forms in client-side Angular applications is different than in classical
20498          * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20499          * page reload that sends the data to the server. Instead some javascript logic should be triggered
20500          * to handle the form submission in an application-specific way.
20501          *
20502          * For this reason, Angular prevents the default action (form submission to the server) unless the
20503          * `<form>` element has an `action` attribute specified.
20504          *
20505          * You can use one of the following two ways to specify what javascript method should be called when
20506          * a form is submitted:
20507          *
20508          * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20509          * - {@link ng.directive:ngClick ngClick} directive on the first
20510           *  button or input field of type submit (input[type=submit])
20511          *
20512          * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20513          * or {@link ng.directive:ngClick ngClick} directives.
20514          * This is because of the following form submission rules in the HTML specification:
20515          *
20516          * - If a form has only one input field then hitting enter in this field triggers form submit
20517          * (`ngSubmit`)
20518          * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20519          * doesn't trigger submit
20520          * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20521          * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20522          * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20523          *
20524          * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20525          * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20526          * to have access to the updated model.
20527          *
20528          * ## Animation Hooks
20529          *
20530          * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20531          * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20532          * other validations that are performed within the form. Animations in ngForm are similar to how
20533          * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20534          * as JS animations.
20535          *
20536          * The following example shows a simple way to utilize CSS transitions to style a form element
20537          * that has been rendered as invalid after it has been validated:
20538          *
20539          * <pre>
20540          * //be sure to include ngAnimate as a module to hook into more
20541          * //advanced animations
20542          * .my-form {
20543          *   transition:0.5s linear all;
20544          *   background: white;
20545          * }
20546          * .my-form.ng-invalid {
20547          *   background: red;
20548          *   color:white;
20549          * }
20550          * </pre>
20551          *
20552          * @example
20553             <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20554               <file name="index.html">
20555                <script>
20556                  angular.module('formExample', [])
20557                    .controller('FormController', ['$scope', function($scope) {
20558                      $scope.userType = 'guest';
20559                    }]);
20560                </script>
20561                <style>
20562                 .my-form {
20563                   transition:all linear 0.5s;
20564                   background: transparent;
20565                 }
20566                 .my-form.ng-invalid {
20567                   background: red;
20568                 }
20569                </style>
20570                <form name="myForm" ng-controller="FormController" class="my-form">
20571                  userType: <input name="input" ng-model="userType" required>
20572                  <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20573                  <code>userType = {{userType}}</code><br>
20574                  <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20575                  <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20576                  <code>myForm.$valid = {{myForm.$valid}}</code><br>
20577                  <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20578                 </form>
20579               </file>
20580               <file name="protractor.js" type="protractor">
20581                 it('should initialize to model', function() {
20582                   var userType = element(by.binding('userType'));
20583                   var valid = element(by.binding('myForm.input.$valid'));
20584
20585                   expect(userType.getText()).toContain('guest');
20586                   expect(valid.getText()).toContain('true');
20587                 });
20588
20589                 it('should be invalid if empty', function() {
20590                   var userType = element(by.binding('userType'));
20591                   var valid = element(by.binding('myForm.input.$valid'));
20592                   var userInput = element(by.model('userType'));
20593
20594                   userInput.clear();
20595                   userInput.sendKeys('');
20596
20597                   expect(userType.getText()).toEqual('userType =');
20598                   expect(valid.getText()).toContain('false');
20599                 });
20600               </file>
20601             </example>
20602          *
20603          * @param {string=} name Name of the form. If specified, the form controller will be published into
20604          *                       related scope, under this name.
20605          */
20606         var formDirectiveFactory = function(isNgForm) {
20607           return ['$timeout', '$parse', function($timeout, $parse) {
20608             var formDirective = {
20609               name: 'form',
20610               restrict: isNgForm ? 'EAC' : 'E',
20611               require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20612               controller: FormController,
20613               compile: function ngFormCompile(formElement, attr) {
20614                 // Setup initial state of the control
20615                 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20616
20617                 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20618
20619                 return {
20620                   pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20621                     var controller = ctrls[0];
20622
20623                     // if `action` attr is not present on the form, prevent the default action (submission)
20624                     if (!('action' in attr)) {
20625                       // we can't use jq events because if a form is destroyed during submission the default
20626                       // action is not prevented. see #1238
20627                       //
20628                       // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20629                       // page reload if the form was destroyed by submission of the form via a click handler
20630                       // on a button in the form. Looks like an IE9 specific bug.
20631                       var handleFormSubmission = function(event) {
20632                         scope.$apply(function() {
20633                           controller.$commitViewValue();
20634                           controller.$setSubmitted();
20635                         });
20636
20637                         event.preventDefault();
20638                       };
20639
20640                       addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20641
20642                       // unregister the preventDefault listener so that we don't not leak memory but in a
20643                       // way that will achieve the prevention of the default action.
20644                       formElement.on('$destroy', function() {
20645                         $timeout(function() {
20646                           removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20647                         }, 0, false);
20648                       });
20649                     }
20650
20651                     var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20652                     parentFormCtrl.$addControl(controller);
20653
20654                     var setter = nameAttr ? getSetter(controller.$name) : noop;
20655
20656                     if (nameAttr) {
20657                       setter(scope, controller);
20658                       attr.$observe(nameAttr, function(newValue) {
20659                         if (controller.$name === newValue) return;
20660                         setter(scope, undefined);
20661                         controller.$$parentForm.$$renameControl(controller, newValue);
20662                         setter = getSetter(controller.$name);
20663                         setter(scope, controller);
20664                       });
20665                     }
20666                     formElement.on('$destroy', function() {
20667                       controller.$$parentForm.$removeControl(controller);
20668                       setter(scope, undefined);
20669                       extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20670                     });
20671                   }
20672                 };
20673               }
20674             };
20675
20676             return formDirective;
20677
20678             function getSetter(expression) {
20679               if (expression === '') {
20680                 //create an assignable expression, so forms with an empty name can be renamed later
20681                 return $parse('this[""]').assign;
20682               }
20683               return $parse(expression).assign || noop;
20684             }
20685           }];
20686         };
20687
20688         var formDirective = formDirectiveFactory();
20689         var ngFormDirective = formDirectiveFactory(true);
20690
20691         /* global VALID_CLASS: false,
20692           INVALID_CLASS: false,
20693           PRISTINE_CLASS: false,
20694           DIRTY_CLASS: false,
20695           UNTOUCHED_CLASS: false,
20696           TOUCHED_CLASS: false,
20697           ngModelMinErr: false,
20698         */
20699
20700         // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20701         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)/;
20702         // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20703         var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20704         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;
20705         var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20706         var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20707         var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20708         var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20709         var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20710         var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20711
20712         var inputType = {
20713
20714           /**
20715            * @ngdoc input
20716            * @name input[text]
20717            *
20718            * @description
20719            * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20720            *
20721            *
20722            * @param {string} ngModel Assignable angular expression to data-bind to.
20723            * @param {string=} name Property name of the form under which the control is published.
20724            * @param {string=} required Adds `required` validation error key if the value is not entered.
20725            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20726            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20727            *    `required` when you want to data-bind to the `required` attribute.
20728            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20729            *    minlength.
20730            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20731            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20732            *    any length.
20733            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20734            *    that contains the regular expression body that will be converted to a regular expression
20735            *    as in the ngPattern directive.
20736            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20737            *    a RegExp found by evaluating the Angular expression given in the attribute value.
20738            *    If the expression evaluates to a RegExp object, then this is used directly.
20739            *    If the expression evaluates to a string, then it will be converted to a RegExp
20740            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20741            *    `new RegExp('^abc$')`.<br />
20742            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20743            *    start at the index of the last search's match, thus not taking the whole input value into
20744            *    account.
20745            * @param {string=} ngChange Angular expression to be executed when input changes due to user
20746            *    interaction with the input element.
20747            * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20748            *    This parameter is ignored for input[type=password] controls, which will never trim the
20749            *    input.
20750            *
20751            * @example
20752               <example name="text-input-directive" module="textInputExample">
20753                 <file name="index.html">
20754                  <script>
20755                    angular.module('textInputExample', [])
20756                      .controller('ExampleController', ['$scope', function($scope) {
20757                        $scope.example = {
20758                          text: 'guest',
20759                          word: /^\s*\w*\s*$/
20760                        };
20761                      }]);
20762                  </script>
20763                  <form name="myForm" ng-controller="ExampleController">
20764                    <label>Single word:
20765                      <input type="text" name="input" ng-model="example.text"
20766                             ng-pattern="example.word" required ng-trim="false">
20767                    </label>
20768                    <div role="alert">
20769                      <span class="error" ng-show="myForm.input.$error.required">
20770                        Required!</span>
20771                      <span class="error" ng-show="myForm.input.$error.pattern">
20772                        Single word only!</span>
20773                    </div>
20774                    <tt>text = {{example.text}}</tt><br/>
20775                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20776                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20777                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20778                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20779                   </form>
20780                 </file>
20781                 <file name="protractor.js" type="protractor">
20782                   var text = element(by.binding('example.text'));
20783                   var valid = element(by.binding('myForm.input.$valid'));
20784                   var input = element(by.model('example.text'));
20785
20786                   it('should initialize to model', function() {
20787                     expect(text.getText()).toContain('guest');
20788                     expect(valid.getText()).toContain('true');
20789                   });
20790
20791                   it('should be invalid if empty', function() {
20792                     input.clear();
20793                     input.sendKeys('');
20794
20795                     expect(text.getText()).toEqual('text =');
20796                     expect(valid.getText()).toContain('false');
20797                   });
20798
20799                   it('should be invalid if multi word', function() {
20800                     input.clear();
20801                     input.sendKeys('hello world');
20802
20803                     expect(valid.getText()).toContain('false');
20804                   });
20805                 </file>
20806               </example>
20807            */
20808           'text': textInputType,
20809
20810             /**
20811              * @ngdoc input
20812              * @name input[date]
20813              *
20814              * @description
20815              * Input with date validation and transformation. In browsers that do not yet support
20816              * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20817              * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20818              * modern browsers do not yet support this input type, it is important to provide cues to users on the
20819              * expected input format via a placeholder or label.
20820              *
20821              * The model must always be a Date object, otherwise Angular will throw an error.
20822              * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20823              *
20824              * The timezone to be used to read/write the `Date` instance in the model can be defined using
20825              * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20826              *
20827              * @param {string} ngModel Assignable angular expression to data-bind to.
20828              * @param {string=} name Property name of the form under which the control is published.
20829              * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20830              *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20831              *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20832              *   constraint validation.
20833              * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20834              *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20835              *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20836              *   constraint validation.
20837              * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20838              *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20839              * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20840              *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20841              * @param {string=} required Sets `required` validation error key if the value is not entered.
20842              * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20843              *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20844              *    `required` when you want to data-bind to the `required` attribute.
20845              * @param {string=} ngChange Angular expression to be executed when input changes due to user
20846              *    interaction with the input element.
20847              *
20848              * @example
20849              <example name="date-input-directive" module="dateInputExample">
20850              <file name="index.html">
20851                <script>
20852                   angular.module('dateInputExample', [])
20853                     .controller('DateController', ['$scope', function($scope) {
20854                       $scope.example = {
20855                         value: new Date(2013, 9, 22)
20856                       };
20857                     }]);
20858                </script>
20859                <form name="myForm" ng-controller="DateController as dateCtrl">
20860                   <label for="exampleInput">Pick a date in 2013:</label>
20861                   <input type="date" id="exampleInput" name="input" ng-model="example.value"
20862                       placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20863                   <div role="alert">
20864                     <span class="error" ng-show="myForm.input.$error.required">
20865                         Required!</span>
20866                     <span class="error" ng-show="myForm.input.$error.date">
20867                         Not a valid date!</span>
20868                    </div>
20869                    <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20870                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20871                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20872                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20873                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20874                </form>
20875              </file>
20876              <file name="protractor.js" type="protractor">
20877                 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20878                 var valid = element(by.binding('myForm.input.$valid'));
20879                 var input = element(by.model('example.value'));
20880
20881                 // currently protractor/webdriver does not support
20882                 // sending keys to all known HTML5 input controls
20883                 // for various browsers (see https://github.com/angular/protractor/issues/562).
20884                 function setInput(val) {
20885                   // set the value of the element and force validation.
20886                   var scr = "var ipt = document.getElementById('exampleInput'); " +
20887                   "ipt.value = '" + val + "';" +
20888                   "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20889                   browser.executeScript(scr);
20890                 }
20891
20892                 it('should initialize to model', function() {
20893                   expect(value.getText()).toContain('2013-10-22');
20894                   expect(valid.getText()).toContain('myForm.input.$valid = true');
20895                 });
20896
20897                 it('should be invalid if empty', function() {
20898                   setInput('');
20899                   expect(value.getText()).toEqual('value =');
20900                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20901                 });
20902
20903                 it('should be invalid if over max', function() {
20904                   setInput('2015-01-01');
20905                   expect(value.getText()).toContain('');
20906                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20907                 });
20908              </file>
20909              </example>
20910              */
20911           'date': createDateInputType('date', DATE_REGEXP,
20912                  createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20913                  'yyyy-MM-dd'),
20914
20915            /**
20916             * @ngdoc input
20917             * @name input[datetime-local]
20918             *
20919             * @description
20920             * Input with datetime validation and transformation. In browsers that do not yet support
20921             * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20922             * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20923             *
20924             * The model must always be a Date object, otherwise Angular will throw an error.
20925             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20926             *
20927             * The timezone to be used to read/write the `Date` instance in the model can be defined using
20928             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20929             *
20930             * @param {string} ngModel Assignable angular expression to data-bind to.
20931             * @param {string=} name Property name of the form under which the control is published.
20932             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20933             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20934             *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20935             *   Note that `min` will also add native HTML5 constraint validation.
20936             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20937             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20938             *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20939             *   Note that `max` will also add native HTML5 constraint validation.
20940             * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20941             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20942             * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20943             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20944             * @param {string=} required Sets `required` validation error key if the value is not entered.
20945             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20946             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20947             *    `required` when you want to data-bind to the `required` attribute.
20948             * @param {string=} ngChange Angular expression to be executed when input changes due to user
20949             *    interaction with the input element.
20950             *
20951             * @example
20952             <example name="datetimelocal-input-directive" module="dateExample">
20953             <file name="index.html">
20954               <script>
20955                 angular.module('dateExample', [])
20956                   .controller('DateController', ['$scope', function($scope) {
20957                     $scope.example = {
20958                       value: new Date(2010, 11, 28, 14, 57)
20959                     };
20960                   }]);
20961               </script>
20962               <form name="myForm" ng-controller="DateController as dateCtrl">
20963                 <label for="exampleInput">Pick a date between in 2013:</label>
20964                 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20965                     placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20966                 <div role="alert">
20967                   <span class="error" ng-show="myForm.input.$error.required">
20968                       Required!</span>
20969                   <span class="error" ng-show="myForm.input.$error.datetimelocal">
20970                       Not a valid date!</span>
20971                 </div>
20972                 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20973                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20974                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20975                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20976                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20977               </form>
20978             </file>
20979             <file name="protractor.js" type="protractor">
20980               var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20981               var valid = element(by.binding('myForm.input.$valid'));
20982               var input = element(by.model('example.value'));
20983
20984               // currently protractor/webdriver does not support
20985               // sending keys to all known HTML5 input controls
20986               // for various browsers (https://github.com/angular/protractor/issues/562).
20987               function setInput(val) {
20988                 // set the value of the element and force validation.
20989                 var scr = "var ipt = document.getElementById('exampleInput'); " +
20990                 "ipt.value = '" + val + "';" +
20991                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20992                 browser.executeScript(scr);
20993               }
20994
20995               it('should initialize to model', function() {
20996                 expect(value.getText()).toContain('2010-12-28T14:57:00');
20997                 expect(valid.getText()).toContain('myForm.input.$valid = true');
20998               });
20999
21000               it('should be invalid if empty', function() {
21001                 setInput('');
21002                 expect(value.getText()).toEqual('value =');
21003                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21004               });
21005
21006               it('should be invalid if over max', function() {
21007                 setInput('2015-01-01T23:59:00');
21008                 expect(value.getText()).toContain('');
21009                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21010               });
21011             </file>
21012             </example>
21013             */
21014           'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21015               createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21016               'yyyy-MM-ddTHH:mm:ss.sss'),
21017
21018           /**
21019            * @ngdoc input
21020            * @name input[time]
21021            *
21022            * @description
21023            * Input with time validation and transformation. In browsers that do not yet support
21024            * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21025            * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21026            * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21027            *
21028            * The model must always be a Date object, otherwise Angular will throw an error.
21029            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21030            *
21031            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21032            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21033            *
21034            * @param {string} ngModel Assignable angular expression to data-bind to.
21035            * @param {string=} name Property name of the form under which the control is published.
21036            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21037            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21038            *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21039            *   native HTML5 constraint validation.
21040            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21041            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21042            *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21043            *   native HTML5 constraint validation.
21044            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21045            *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21046            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21047            *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21048            * @param {string=} required Sets `required` validation error key if the value is not entered.
21049            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21050            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21051            *    `required` when you want to data-bind to the `required` attribute.
21052            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21053            *    interaction with the input element.
21054            *
21055            * @example
21056            <example name="time-input-directive" module="timeExample">
21057            <file name="index.html">
21058              <script>
21059               angular.module('timeExample', [])
21060                 .controller('DateController', ['$scope', function($scope) {
21061                   $scope.example = {
21062                     value: new Date(1970, 0, 1, 14, 57, 0)
21063                   };
21064                 }]);
21065              </script>
21066              <form name="myForm" ng-controller="DateController as dateCtrl">
21067                 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21068                 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21069                     placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21070                 <div role="alert">
21071                   <span class="error" ng-show="myForm.input.$error.required">
21072                       Required!</span>
21073                   <span class="error" ng-show="myForm.input.$error.time">
21074                       Not a valid date!</span>
21075                 </div>
21076                 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21077                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21078                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21079                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21080                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21081              </form>
21082            </file>
21083            <file name="protractor.js" type="protractor">
21084               var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21085               var valid = element(by.binding('myForm.input.$valid'));
21086               var input = element(by.model('example.value'));
21087
21088               // currently protractor/webdriver does not support
21089               // sending keys to all known HTML5 input controls
21090               // for various browsers (https://github.com/angular/protractor/issues/562).
21091               function setInput(val) {
21092                 // set the value of the element and force validation.
21093                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21094                 "ipt.value = '" + val + "';" +
21095                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21096                 browser.executeScript(scr);
21097               }
21098
21099               it('should initialize to model', function() {
21100                 expect(value.getText()).toContain('14:57:00');
21101                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21102               });
21103
21104               it('should be invalid if empty', function() {
21105                 setInput('');
21106                 expect(value.getText()).toEqual('value =');
21107                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21108               });
21109
21110               it('should be invalid if over max', function() {
21111                 setInput('23:59:00');
21112                 expect(value.getText()).toContain('');
21113                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21114               });
21115            </file>
21116            </example>
21117            */
21118           'time': createDateInputType('time', TIME_REGEXP,
21119               createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21120              'HH:mm:ss.sss'),
21121
21122            /**
21123             * @ngdoc input
21124             * @name input[week]
21125             *
21126             * @description
21127             * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21128             * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21129             * week format (yyyy-W##), for example: `2013-W02`.
21130             *
21131             * The model must always be a Date object, otherwise Angular will throw an error.
21132             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21133             *
21134             * The timezone to be used to read/write the `Date` instance in the model can be defined using
21135             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21136             *
21137             * @param {string} ngModel Assignable angular expression to data-bind to.
21138             * @param {string=} name Property name of the form under which the control is published.
21139             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21140             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21141             *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21142             *   native HTML5 constraint validation.
21143             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21144             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21145             *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21146             *   native HTML5 constraint validation.
21147             * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21148             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21149             * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21150             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21151             * @param {string=} required Sets `required` validation error key if the value is not entered.
21152             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21153             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21154             *    `required` when you want to data-bind to the `required` attribute.
21155             * @param {string=} ngChange Angular expression to be executed when input changes due to user
21156             *    interaction with the input element.
21157             *
21158             * @example
21159             <example name="week-input-directive" module="weekExample">
21160             <file name="index.html">
21161               <script>
21162               angular.module('weekExample', [])
21163                 .controller('DateController', ['$scope', function($scope) {
21164                   $scope.example = {
21165                     value: new Date(2013, 0, 3)
21166                   };
21167                 }]);
21168               </script>
21169               <form name="myForm" ng-controller="DateController as dateCtrl">
21170                 <label>Pick a date between in 2013:
21171                   <input id="exampleInput" type="week" name="input" ng-model="example.value"
21172                          placeholder="YYYY-W##" min="2012-W32"
21173                          max="2013-W52" required />
21174                 </label>
21175                 <div role="alert">
21176                   <span class="error" ng-show="myForm.input.$error.required">
21177                       Required!</span>
21178                   <span class="error" ng-show="myForm.input.$error.week">
21179                       Not a valid date!</span>
21180                 </div>
21181                 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21182                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21183                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21184                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21185                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21186               </form>
21187             </file>
21188             <file name="protractor.js" type="protractor">
21189               var value = element(by.binding('example.value | date: "yyyy-Www"'));
21190               var valid = element(by.binding('myForm.input.$valid'));
21191               var input = element(by.model('example.value'));
21192
21193               // currently protractor/webdriver does not support
21194               // sending keys to all known HTML5 input controls
21195               // for various browsers (https://github.com/angular/protractor/issues/562).
21196               function setInput(val) {
21197                 // set the value of the element and force validation.
21198                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21199                 "ipt.value = '" + val + "';" +
21200                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21201                 browser.executeScript(scr);
21202               }
21203
21204               it('should initialize to model', function() {
21205                 expect(value.getText()).toContain('2013-W01');
21206                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21207               });
21208
21209               it('should be invalid if empty', function() {
21210                 setInput('');
21211                 expect(value.getText()).toEqual('value =');
21212                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21213               });
21214
21215               it('should be invalid if over max', function() {
21216                 setInput('2015-W01');
21217                 expect(value.getText()).toContain('');
21218                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21219               });
21220             </file>
21221             </example>
21222             */
21223           'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21224
21225           /**
21226            * @ngdoc input
21227            * @name input[month]
21228            *
21229            * @description
21230            * Input with month validation and transformation. In browsers that do not yet support
21231            * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21232            * month format (yyyy-MM), for example: `2009-01`.
21233            *
21234            * The model must always be a Date object, otherwise Angular will throw an error.
21235            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21236            * If the model is not set to the first of the month, the next view to model update will set it
21237            * to the first of the month.
21238            *
21239            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21240            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21241            *
21242            * @param {string} ngModel Assignable angular expression to data-bind to.
21243            * @param {string=} name Property name of the form under which the control is published.
21244            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21245            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21246            *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21247            *   native HTML5 constraint validation.
21248            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21249            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21250            *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21251            *   native HTML5 constraint validation.
21252            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21253            *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21254            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21255            *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21256
21257            * @param {string=} required Sets `required` validation error key if the value is not entered.
21258            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21259            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21260            *    `required` when you want to data-bind to the `required` attribute.
21261            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21262            *    interaction with the input element.
21263            *
21264            * @example
21265            <example name="month-input-directive" module="monthExample">
21266            <file name="index.html">
21267              <script>
21268               angular.module('monthExample', [])
21269                 .controller('DateController', ['$scope', function($scope) {
21270                   $scope.example = {
21271                     value: new Date(2013, 9, 1)
21272                   };
21273                 }]);
21274              </script>
21275              <form name="myForm" ng-controller="DateController as dateCtrl">
21276                <label for="exampleInput">Pick a month in 2013:</label>
21277                <input id="exampleInput" type="month" name="input" ng-model="example.value"
21278                   placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21279                <div role="alert">
21280                  <span class="error" ng-show="myForm.input.$error.required">
21281                     Required!</span>
21282                  <span class="error" ng-show="myForm.input.$error.month">
21283                     Not a valid month!</span>
21284                </div>
21285                <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21286                <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21287                <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21288                <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21289                <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21290              </form>
21291            </file>
21292            <file name="protractor.js" type="protractor">
21293               var value = element(by.binding('example.value | date: "yyyy-MM"'));
21294               var valid = element(by.binding('myForm.input.$valid'));
21295               var input = element(by.model('example.value'));
21296
21297               // currently protractor/webdriver does not support
21298               // sending keys to all known HTML5 input controls
21299               // for various browsers (https://github.com/angular/protractor/issues/562).
21300               function setInput(val) {
21301                 // set the value of the element and force validation.
21302                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21303                 "ipt.value = '" + val + "';" +
21304                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21305                 browser.executeScript(scr);
21306               }
21307
21308               it('should initialize to model', function() {
21309                 expect(value.getText()).toContain('2013-10');
21310                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21311               });
21312
21313               it('should be invalid if empty', function() {
21314                 setInput('');
21315                 expect(value.getText()).toEqual('value =');
21316                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21317               });
21318
21319               it('should be invalid if over max', function() {
21320                 setInput('2015-01');
21321                 expect(value.getText()).toContain('');
21322                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21323               });
21324            </file>
21325            </example>
21326            */
21327           'month': createDateInputType('month', MONTH_REGEXP,
21328              createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21329              'yyyy-MM'),
21330
21331           /**
21332            * @ngdoc input
21333            * @name input[number]
21334            *
21335            * @description
21336            * Text input with number validation and transformation. Sets the `number` validation
21337            * error if not a valid number.
21338            *
21339            * <div class="alert alert-warning">
21340            * The model must always be of type `number` otherwise Angular will throw an error.
21341            * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21342            * error docs for more information and an example of how to convert your model if necessary.
21343            * </div>
21344            *
21345            * ## Issues with HTML5 constraint validation
21346            *
21347            * In browsers that follow the
21348            * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21349            * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21350            * If a non-number is entered in the input, the browser will report the value as an empty string,
21351            * which means the view / model values in `ngModel` and subsequently the scope value
21352            * will also be an empty string.
21353            *
21354            *
21355            * @param {string} ngModel Assignable angular expression to data-bind to.
21356            * @param {string=} name Property name of the form under which the control is published.
21357            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21358            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21359            * @param {string=} required Sets `required` validation error key if the value is not entered.
21360            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21361            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21362            *    `required` when you want to data-bind to the `required` attribute.
21363            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21364            *    minlength.
21365            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21366            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21367            *    any length.
21368            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21369            *    that contains the regular expression body that will be converted to a regular expression
21370            *    as in the ngPattern directive.
21371            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21372            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21373            *    If the expression evaluates to a RegExp object, then this is used directly.
21374            *    If the expression evaluates to a string, then it will be converted to a RegExp
21375            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21376            *    `new RegExp('^abc$')`.<br />
21377            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21378            *    start at the index of the last search's match, thus not taking the whole input value into
21379            *    account.
21380            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21381            *    interaction with the input element.
21382            *
21383            * @example
21384               <example name="number-input-directive" module="numberExample">
21385                 <file name="index.html">
21386                  <script>
21387                    angular.module('numberExample', [])
21388                      .controller('ExampleController', ['$scope', function($scope) {
21389                        $scope.example = {
21390                          value: 12
21391                        };
21392                      }]);
21393                  </script>
21394                  <form name="myForm" ng-controller="ExampleController">
21395                    <label>Number:
21396                      <input type="number" name="input" ng-model="example.value"
21397                             min="0" max="99" required>
21398                   </label>
21399                    <div role="alert">
21400                      <span class="error" ng-show="myForm.input.$error.required">
21401                        Required!</span>
21402                      <span class="error" ng-show="myForm.input.$error.number">
21403                        Not valid number!</span>
21404                    </div>
21405                    <tt>value = {{example.value}}</tt><br/>
21406                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21407                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21408                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21409                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21410                   </form>
21411                 </file>
21412                 <file name="protractor.js" type="protractor">
21413                   var value = element(by.binding('example.value'));
21414                   var valid = element(by.binding('myForm.input.$valid'));
21415                   var input = element(by.model('example.value'));
21416
21417                   it('should initialize to model', function() {
21418                     expect(value.getText()).toContain('12');
21419                     expect(valid.getText()).toContain('true');
21420                   });
21421
21422                   it('should be invalid if empty', function() {
21423                     input.clear();
21424                     input.sendKeys('');
21425                     expect(value.getText()).toEqual('value =');
21426                     expect(valid.getText()).toContain('false');
21427                   });
21428
21429                   it('should be invalid if over max', function() {
21430                     input.clear();
21431                     input.sendKeys('123');
21432                     expect(value.getText()).toEqual('value =');
21433                     expect(valid.getText()).toContain('false');
21434                   });
21435                 </file>
21436               </example>
21437            */
21438           'number': numberInputType,
21439
21440
21441           /**
21442            * @ngdoc input
21443            * @name input[url]
21444            *
21445            * @description
21446            * Text input with URL validation. Sets the `url` validation error key if the content is not a
21447            * valid URL.
21448            *
21449            * <div class="alert alert-warning">
21450            * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21451            * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21452            * the built-in validators (see the {@link guide/forms Forms guide})
21453            * </div>
21454            *
21455            * @param {string} ngModel Assignable angular expression to data-bind to.
21456            * @param {string=} name Property name of the form under which the control is published.
21457            * @param {string=} required Sets `required` validation error key if the value is not entered.
21458            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21459            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21460            *    `required` when you want to data-bind to the `required` attribute.
21461            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21462            *    minlength.
21463            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21464            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21465            *    any length.
21466            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21467            *    that contains the regular expression body that will be converted to a regular expression
21468            *    as in the ngPattern directive.
21469            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21470            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21471            *    If the expression evaluates to a RegExp object, then this is used directly.
21472            *    If the expression evaluates to a string, then it will be converted to a RegExp
21473            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21474            *    `new RegExp('^abc$')`.<br />
21475            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21476            *    start at the index of the last search's match, thus not taking the whole input value into
21477            *    account.
21478            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21479            *    interaction with the input element.
21480            *
21481            * @example
21482               <example name="url-input-directive" module="urlExample">
21483                 <file name="index.html">
21484                  <script>
21485                    angular.module('urlExample', [])
21486                      .controller('ExampleController', ['$scope', function($scope) {
21487                        $scope.url = {
21488                          text: 'http://google.com'
21489                        };
21490                      }]);
21491                  </script>
21492                  <form name="myForm" ng-controller="ExampleController">
21493                    <label>URL:
21494                      <input type="url" name="input" ng-model="url.text" required>
21495                    <label>
21496                    <div role="alert">
21497                      <span class="error" ng-show="myForm.input.$error.required">
21498                        Required!</span>
21499                      <span class="error" ng-show="myForm.input.$error.url">
21500                        Not valid url!</span>
21501                    </div>
21502                    <tt>text = {{url.text}}</tt><br/>
21503                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21504                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21505                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21506                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21507                    <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21508                   </form>
21509                 </file>
21510                 <file name="protractor.js" type="protractor">
21511                   var text = element(by.binding('url.text'));
21512                   var valid = element(by.binding('myForm.input.$valid'));
21513                   var input = element(by.model('url.text'));
21514
21515                   it('should initialize to model', function() {
21516                     expect(text.getText()).toContain('http://google.com');
21517                     expect(valid.getText()).toContain('true');
21518                   });
21519
21520                   it('should be invalid if empty', function() {
21521                     input.clear();
21522                     input.sendKeys('');
21523
21524                     expect(text.getText()).toEqual('text =');
21525                     expect(valid.getText()).toContain('false');
21526                   });
21527
21528                   it('should be invalid if not url', function() {
21529                     input.clear();
21530                     input.sendKeys('box');
21531
21532                     expect(valid.getText()).toContain('false');
21533                   });
21534                 </file>
21535               </example>
21536            */
21537           'url': urlInputType,
21538
21539
21540           /**
21541            * @ngdoc input
21542            * @name input[email]
21543            *
21544            * @description
21545            * Text input with email validation. Sets the `email` validation error key if not a valid email
21546            * address.
21547            *
21548            * <div class="alert alert-warning">
21549            * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21550            * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21551            * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21552            * </div>
21553            *
21554            * @param {string} ngModel Assignable angular expression to data-bind to.
21555            * @param {string=} name Property name of the form under which the control is published.
21556            * @param {string=} required Sets `required` validation error key if the value is not entered.
21557            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21558            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21559            *    `required` when you want to data-bind to the `required` attribute.
21560            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21561            *    minlength.
21562            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21563            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21564            *    any length.
21565            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21566            *    that contains the regular expression body that will be converted to a regular expression
21567            *    as in the ngPattern directive.
21568            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21569            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21570            *    If the expression evaluates to a RegExp object, then this is used directly.
21571            *    If the expression evaluates to a string, then it will be converted to a RegExp
21572            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21573            *    `new RegExp('^abc$')`.<br />
21574            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21575            *    start at the index of the last search's match, thus not taking the whole input value into
21576            *    account.
21577            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21578            *    interaction with the input element.
21579            *
21580            * @example
21581               <example name="email-input-directive" module="emailExample">
21582                 <file name="index.html">
21583                  <script>
21584                    angular.module('emailExample', [])
21585                      .controller('ExampleController', ['$scope', function($scope) {
21586                        $scope.email = {
21587                          text: 'me@example.com'
21588                        };
21589                      }]);
21590                  </script>
21591                    <form name="myForm" ng-controller="ExampleController">
21592                      <label>Email:
21593                        <input type="email" name="input" ng-model="email.text" required>
21594                      </label>
21595                      <div role="alert">
21596                        <span class="error" ng-show="myForm.input.$error.required">
21597                          Required!</span>
21598                        <span class="error" ng-show="myForm.input.$error.email">
21599                          Not valid email!</span>
21600                      </div>
21601                      <tt>text = {{email.text}}</tt><br/>
21602                      <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21603                      <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21604                      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21605                      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21606                      <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21607                    </form>
21608                  </file>
21609                 <file name="protractor.js" type="protractor">
21610                   var text = element(by.binding('email.text'));
21611                   var valid = element(by.binding('myForm.input.$valid'));
21612                   var input = element(by.model('email.text'));
21613
21614                   it('should initialize to model', function() {
21615                     expect(text.getText()).toContain('me@example.com');
21616                     expect(valid.getText()).toContain('true');
21617                   });
21618
21619                   it('should be invalid if empty', function() {
21620                     input.clear();
21621                     input.sendKeys('');
21622                     expect(text.getText()).toEqual('text =');
21623                     expect(valid.getText()).toContain('false');
21624                   });
21625
21626                   it('should be invalid if not email', function() {
21627                     input.clear();
21628                     input.sendKeys('xxx');
21629
21630                     expect(valid.getText()).toContain('false');
21631                   });
21632                 </file>
21633               </example>
21634            */
21635           'email': emailInputType,
21636
21637
21638           /**
21639            * @ngdoc input
21640            * @name input[radio]
21641            *
21642            * @description
21643            * HTML radio button.
21644            *
21645            * @param {string} ngModel Assignable angular expression to data-bind to.
21646            * @param {string} value The value to which the `ngModel` expression should be set when selected.
21647            *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21648            *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21649            * @param {string=} name Property name of the form under which the control is published.
21650            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21651            *    interaction with the input element.
21652            * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21653            *    is selected. Should be used instead of the `value` attribute if you need
21654            *    a non-string `ngModel` (`boolean`, `array`, ...).
21655            *
21656            * @example
21657               <example name="radio-input-directive" module="radioExample">
21658                 <file name="index.html">
21659                  <script>
21660                    angular.module('radioExample', [])
21661                      .controller('ExampleController', ['$scope', function($scope) {
21662                        $scope.color = {
21663                          name: 'blue'
21664                        };
21665                        $scope.specialValue = {
21666                          "id": "12345",
21667                          "value": "green"
21668                        };
21669                      }]);
21670                  </script>
21671                  <form name="myForm" ng-controller="ExampleController">
21672                    <label>
21673                      <input type="radio" ng-model="color.name" value="red">
21674                      Red
21675                    </label><br/>
21676                    <label>
21677                      <input type="radio" ng-model="color.name" ng-value="specialValue">
21678                      Green
21679                    </label><br/>
21680                    <label>
21681                      <input type="radio" ng-model="color.name" value="blue">
21682                      Blue
21683                    </label><br/>
21684                    <tt>color = {{color.name | json}}</tt><br/>
21685                   </form>
21686                   Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21687                 </file>
21688                 <file name="protractor.js" type="protractor">
21689                   it('should change state', function() {
21690                     var color = element(by.binding('color.name'));
21691
21692                     expect(color.getText()).toContain('blue');
21693
21694                     element.all(by.model('color.name')).get(0).click();
21695
21696                     expect(color.getText()).toContain('red');
21697                   });
21698                 </file>
21699               </example>
21700            */
21701           'radio': radioInputType,
21702
21703
21704           /**
21705            * @ngdoc input
21706            * @name input[checkbox]
21707            *
21708            * @description
21709            * HTML checkbox.
21710            *
21711            * @param {string} ngModel Assignable angular expression to data-bind to.
21712            * @param {string=} name Property name of the form under which the control is published.
21713            * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21714            * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21715            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21716            *    interaction with the input element.
21717            *
21718            * @example
21719               <example name="checkbox-input-directive" module="checkboxExample">
21720                 <file name="index.html">
21721                  <script>
21722                    angular.module('checkboxExample', [])
21723                      .controller('ExampleController', ['$scope', function($scope) {
21724                        $scope.checkboxModel = {
21725                         value1 : true,
21726                         value2 : 'YES'
21727                       };
21728                      }]);
21729                  </script>
21730                  <form name="myForm" ng-controller="ExampleController">
21731                    <label>Value1:
21732                      <input type="checkbox" ng-model="checkboxModel.value1">
21733                    </label><br/>
21734                    <label>Value2:
21735                      <input type="checkbox" ng-model="checkboxModel.value2"
21736                             ng-true-value="'YES'" ng-false-value="'NO'">
21737                     </label><br/>
21738                    <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21739                    <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21740                   </form>
21741                 </file>
21742                 <file name="protractor.js" type="protractor">
21743                   it('should change state', function() {
21744                     var value1 = element(by.binding('checkboxModel.value1'));
21745                     var value2 = element(by.binding('checkboxModel.value2'));
21746
21747                     expect(value1.getText()).toContain('true');
21748                     expect(value2.getText()).toContain('YES');
21749
21750                     element(by.model('checkboxModel.value1')).click();
21751                     element(by.model('checkboxModel.value2')).click();
21752
21753                     expect(value1.getText()).toContain('false');
21754                     expect(value2.getText()).toContain('NO');
21755                   });
21756                 </file>
21757               </example>
21758            */
21759           'checkbox': checkboxInputType,
21760
21761           'hidden': noop,
21762           'button': noop,
21763           'submit': noop,
21764           'reset': noop,
21765           'file': noop
21766         };
21767
21768         function stringBasedInputType(ctrl) {
21769           ctrl.$formatters.push(function(value) {
21770             return ctrl.$isEmpty(value) ? value : value.toString();
21771           });
21772         }
21773
21774         function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21775           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21776           stringBasedInputType(ctrl);
21777         }
21778
21779         function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21780           var type = lowercase(element[0].type);
21781
21782           // In composition mode, users are still inputing intermediate text buffer,
21783           // hold the listener until composition is done.
21784           // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21785           if (!$sniffer.android) {
21786             var composing = false;
21787
21788             element.on('compositionstart', function(data) {
21789               composing = true;
21790             });
21791
21792             element.on('compositionend', function() {
21793               composing = false;
21794               listener();
21795             });
21796           }
21797
21798           var listener = function(ev) {
21799             if (timeout) {
21800               $browser.defer.cancel(timeout);
21801               timeout = null;
21802             }
21803             if (composing) return;
21804             var value = element.val(),
21805                 event = ev && ev.type;
21806
21807             // By default we will trim the value
21808             // If the attribute ng-trim exists we will avoid trimming
21809             // If input type is 'password', the value is never trimmed
21810             if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21811               value = trim(value);
21812             }
21813
21814             // If a control is suffering from bad input (due to native validators), browsers discard its
21815             // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21816             // control's value is the same empty value twice in a row.
21817             if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21818               ctrl.$setViewValue(value, event);
21819             }
21820           };
21821
21822           // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21823           // input event on backspace, delete or cut
21824           if ($sniffer.hasEvent('input')) {
21825             element.on('input', listener);
21826           } else {
21827             var timeout;
21828
21829             var deferListener = function(ev, input, origValue) {
21830               if (!timeout) {
21831                 timeout = $browser.defer(function() {
21832                   timeout = null;
21833                   if (!input || input.value !== origValue) {
21834                     listener(ev);
21835                   }
21836                 });
21837               }
21838             };
21839
21840             element.on('keydown', function(event) {
21841               var key = event.keyCode;
21842
21843               // ignore
21844               //    command            modifiers                   arrows
21845               if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21846
21847               deferListener(event, this, this.value);
21848             });
21849
21850             // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21851             if ($sniffer.hasEvent('paste')) {
21852               element.on('paste cut', deferListener);
21853             }
21854           }
21855
21856           // if user paste into input using mouse on older browser
21857           // or form autocomplete on newer browser, we need "change" event to catch it
21858           element.on('change', listener);
21859
21860           ctrl.$render = function() {
21861             // Workaround for Firefox validation #12102.
21862             var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21863             if (element.val() !== value) {
21864               element.val(value);
21865             }
21866           };
21867         }
21868
21869         function weekParser(isoWeek, existingDate) {
21870           if (isDate(isoWeek)) {
21871             return isoWeek;
21872           }
21873
21874           if (isString(isoWeek)) {
21875             WEEK_REGEXP.lastIndex = 0;
21876             var parts = WEEK_REGEXP.exec(isoWeek);
21877             if (parts) {
21878               var year = +parts[1],
21879                   week = +parts[2],
21880                   hours = 0,
21881                   minutes = 0,
21882                   seconds = 0,
21883                   milliseconds = 0,
21884                   firstThurs = getFirstThursdayOfYear(year),
21885                   addDays = (week - 1) * 7;
21886
21887               if (existingDate) {
21888                 hours = existingDate.getHours();
21889                 minutes = existingDate.getMinutes();
21890                 seconds = existingDate.getSeconds();
21891                 milliseconds = existingDate.getMilliseconds();
21892               }
21893
21894               return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21895             }
21896           }
21897
21898           return NaN;
21899         }
21900
21901         function createDateParser(regexp, mapping) {
21902           return function(iso, date) {
21903             var parts, map;
21904
21905             if (isDate(iso)) {
21906               return iso;
21907             }
21908
21909             if (isString(iso)) {
21910               // When a date is JSON'ified to wraps itself inside of an extra
21911               // set of double quotes. This makes the date parsing code unable
21912               // to match the date string and parse it as a date.
21913               if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21914                 iso = iso.substring(1, iso.length - 1);
21915               }
21916               if (ISO_DATE_REGEXP.test(iso)) {
21917                 return new Date(iso);
21918               }
21919               regexp.lastIndex = 0;
21920               parts = regexp.exec(iso);
21921
21922               if (parts) {
21923                 parts.shift();
21924                 if (date) {
21925                   map = {
21926                     yyyy: date.getFullYear(),
21927                     MM: date.getMonth() + 1,
21928                     dd: date.getDate(),
21929                     HH: date.getHours(),
21930                     mm: date.getMinutes(),
21931                     ss: date.getSeconds(),
21932                     sss: date.getMilliseconds() / 1000
21933                   };
21934                 } else {
21935                   map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21936                 }
21937
21938                 forEach(parts, function(part, index) {
21939                   if (index < mapping.length) {
21940                     map[mapping[index]] = +part;
21941                   }
21942                 });
21943                 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21944               }
21945             }
21946
21947             return NaN;
21948           };
21949         }
21950
21951         function createDateInputType(type, regexp, parseDate, format) {
21952           return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21953             badInputChecker(scope, element, attr, ctrl);
21954             baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21955             var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21956             var previousDate;
21957
21958             ctrl.$$parserName = type;
21959             ctrl.$parsers.push(function(value) {
21960               if (ctrl.$isEmpty(value)) return null;
21961               if (regexp.test(value)) {
21962                 // Note: We cannot read ctrl.$modelValue, as there might be a different
21963                 // parser/formatter in the processing chain so that the model
21964                 // contains some different data format!
21965                 var parsedDate = parseDate(value, previousDate);
21966                 if (timezone) {
21967                   parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21968                 }
21969                 return parsedDate;
21970               }
21971               return undefined;
21972             });
21973
21974             ctrl.$formatters.push(function(value) {
21975               if (value && !isDate(value)) {
21976                 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21977               }
21978               if (isValidDate(value)) {
21979                 previousDate = value;
21980                 if (previousDate && timezone) {
21981                   previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21982                 }
21983                 return $filter('date')(value, format, timezone);
21984               } else {
21985                 previousDate = null;
21986                 return '';
21987               }
21988             });
21989
21990             if (isDefined(attr.min) || attr.ngMin) {
21991               var minVal;
21992               ctrl.$validators.min = function(value) {
21993                 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21994               };
21995               attr.$observe('min', function(val) {
21996                 minVal = parseObservedDateValue(val);
21997                 ctrl.$validate();
21998               });
21999             }
22000
22001             if (isDefined(attr.max) || attr.ngMax) {
22002               var maxVal;
22003               ctrl.$validators.max = function(value) {
22004                 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22005               };
22006               attr.$observe('max', function(val) {
22007                 maxVal = parseObservedDateValue(val);
22008                 ctrl.$validate();
22009               });
22010             }
22011
22012             function isValidDate(value) {
22013               // Invalid Date: getTime() returns NaN
22014               return value && !(value.getTime && value.getTime() !== value.getTime());
22015             }
22016
22017             function parseObservedDateValue(val) {
22018               return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22019             }
22020           };
22021         }
22022
22023         function badInputChecker(scope, element, attr, ctrl) {
22024           var node = element[0];
22025           var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22026           if (nativeValidation) {
22027             ctrl.$parsers.push(function(value) {
22028               var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22029               // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22030               // - also sets validity.badInput (should only be validity.typeMismatch).
22031               // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22032               // - can ignore this case as we can still read out the erroneous email...
22033               return validity.badInput && !validity.typeMismatch ? undefined : value;
22034             });
22035           }
22036         }
22037
22038         function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22039           badInputChecker(scope, element, attr, ctrl);
22040           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22041
22042           ctrl.$$parserName = 'number';
22043           ctrl.$parsers.push(function(value) {
22044             if (ctrl.$isEmpty(value))      return null;
22045             if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22046             return undefined;
22047           });
22048
22049           ctrl.$formatters.push(function(value) {
22050             if (!ctrl.$isEmpty(value)) {
22051               if (!isNumber(value)) {
22052                 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22053               }
22054               value = value.toString();
22055             }
22056             return value;
22057           });
22058
22059           if (isDefined(attr.min) || attr.ngMin) {
22060             var minVal;
22061             ctrl.$validators.min = function(value) {
22062               return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22063             };
22064
22065             attr.$observe('min', function(val) {
22066               if (isDefined(val) && !isNumber(val)) {
22067                 val = parseFloat(val, 10);
22068               }
22069               minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22070               // TODO(matsko): implement validateLater to reduce number of validations
22071               ctrl.$validate();
22072             });
22073           }
22074
22075           if (isDefined(attr.max) || attr.ngMax) {
22076             var maxVal;
22077             ctrl.$validators.max = function(value) {
22078               return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22079             };
22080
22081             attr.$observe('max', function(val) {
22082               if (isDefined(val) && !isNumber(val)) {
22083                 val = parseFloat(val, 10);
22084               }
22085               maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22086               // TODO(matsko): implement validateLater to reduce number of validations
22087               ctrl.$validate();
22088             });
22089           }
22090         }
22091
22092         function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22093           // Note: no badInputChecker here by purpose as `url` is only a validation
22094           // in browsers, i.e. we can always read out input.value even if it is not valid!
22095           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22096           stringBasedInputType(ctrl);
22097
22098           ctrl.$$parserName = 'url';
22099           ctrl.$validators.url = function(modelValue, viewValue) {
22100             var value = modelValue || viewValue;
22101             return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22102           };
22103         }
22104
22105         function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22106           // Note: no badInputChecker here by purpose as `url` is only a validation
22107           // in browsers, i.e. we can always read out input.value even if it is not valid!
22108           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22109           stringBasedInputType(ctrl);
22110
22111           ctrl.$$parserName = 'email';
22112           ctrl.$validators.email = function(modelValue, viewValue) {
22113             var value = modelValue || viewValue;
22114             return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22115           };
22116         }
22117
22118         function radioInputType(scope, element, attr, ctrl) {
22119           // make the name unique, if not defined
22120           if (isUndefined(attr.name)) {
22121             element.attr('name', nextUid());
22122           }
22123
22124           var listener = function(ev) {
22125             if (element[0].checked) {
22126               ctrl.$setViewValue(attr.value, ev && ev.type);
22127             }
22128           };
22129
22130           element.on('click', listener);
22131
22132           ctrl.$render = function() {
22133             var value = attr.value;
22134             element[0].checked = (value == ctrl.$viewValue);
22135           };
22136
22137           attr.$observe('value', ctrl.$render);
22138         }
22139
22140         function parseConstantExpr($parse, context, name, expression, fallback) {
22141           var parseFn;
22142           if (isDefined(expression)) {
22143             parseFn = $parse(expression);
22144             if (!parseFn.constant) {
22145               throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22146                                            '`{1}`.', name, expression);
22147             }
22148             return parseFn(context);
22149           }
22150           return fallback;
22151         }
22152
22153         function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22154           var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22155           var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22156
22157           var listener = function(ev) {
22158             ctrl.$setViewValue(element[0].checked, ev && ev.type);
22159           };
22160
22161           element.on('click', listener);
22162
22163           ctrl.$render = function() {
22164             element[0].checked = ctrl.$viewValue;
22165           };
22166
22167           // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22168           // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22169           // it to a boolean.
22170           ctrl.$isEmpty = function(value) {
22171             return value === false;
22172           };
22173
22174           ctrl.$formatters.push(function(value) {
22175             return equals(value, trueValue);
22176           });
22177
22178           ctrl.$parsers.push(function(value) {
22179             return value ? trueValue : falseValue;
22180           });
22181         }
22182
22183
22184         /**
22185          * @ngdoc directive
22186          * @name textarea
22187          * @restrict E
22188          *
22189          * @description
22190          * HTML textarea element control with angular data-binding. The data-binding and validation
22191          * properties of this element are exactly the same as those of the
22192          * {@link ng.directive:input input element}.
22193          *
22194          * @param {string} ngModel Assignable angular expression to data-bind to.
22195          * @param {string=} name Property name of the form under which the control is published.
22196          * @param {string=} required Sets `required` validation error key if the value is not entered.
22197          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22198          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22199          *    `required` when you want to data-bind to the `required` attribute.
22200          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22201          *    minlength.
22202          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22203          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22204          *    length.
22205          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22206          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22207          *    If the expression evaluates to a RegExp object, then this is used directly.
22208          *    If the expression evaluates to a string, then it will be converted to a RegExp
22209          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22210          *    `new RegExp('^abc$')`.<br />
22211          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22212          *    start at the index of the last search's match, thus not taking the whole input value into
22213          *    account.
22214          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22215          *    interaction with the input element.
22216          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22217          */
22218
22219
22220         /**
22221          * @ngdoc directive
22222          * @name input
22223          * @restrict E
22224          *
22225          * @description
22226          * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22227          * input state control, and validation.
22228          * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22229          *
22230          * <div class="alert alert-warning">
22231          * **Note:** Not every feature offered is available for all input types.
22232          * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22233          * </div>
22234          *
22235          * @param {string} ngModel Assignable angular expression to data-bind to.
22236          * @param {string=} name Property name of the form under which the control is published.
22237          * @param {string=} required Sets `required` validation error key if the value is not entered.
22238          * @param {boolean=} ngRequired Sets `required` attribute if set to true
22239          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22240          *    minlength.
22241          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22242          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22243          *    length.
22244          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22245          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22246          *    If the expression evaluates to a RegExp object, then this is used directly.
22247          *    If the expression evaluates to a string, then it will be converted to a RegExp
22248          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22249          *    `new RegExp('^abc$')`.<br />
22250          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22251          *    start at the index of the last search's match, thus not taking the whole input value into
22252          *    account.
22253          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22254          *    interaction with the input element.
22255          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22256          *    This parameter is ignored for input[type=password] controls, which will never trim the
22257          *    input.
22258          *
22259          * @example
22260             <example name="input-directive" module="inputExample">
22261               <file name="index.html">
22262                <script>
22263                   angular.module('inputExample', [])
22264                     .controller('ExampleController', ['$scope', function($scope) {
22265                       $scope.user = {name: 'guest', last: 'visitor'};
22266                     }]);
22267                </script>
22268                <div ng-controller="ExampleController">
22269                  <form name="myForm">
22270                    <label>
22271                       User name:
22272                       <input type="text" name="userName" ng-model="user.name" required>
22273                    </label>
22274                    <div role="alert">
22275                      <span class="error" ng-show="myForm.userName.$error.required">
22276                       Required!</span>
22277                    </div>
22278                    <label>
22279                       Last name:
22280                       <input type="text" name="lastName" ng-model="user.last"
22281                       ng-minlength="3" ng-maxlength="10">
22282                    </label>
22283                    <div role="alert">
22284                      <span class="error" ng-show="myForm.lastName.$error.minlength">
22285                        Too short!</span>
22286                      <span class="error" ng-show="myForm.lastName.$error.maxlength">
22287                        Too long!</span>
22288                    </div>
22289                  </form>
22290                  <hr>
22291                  <tt>user = {{user}}</tt><br/>
22292                  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22293                  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22294                  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22295                  <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22296                  <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22297                  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22298                  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22299                  <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22300                </div>
22301               </file>
22302               <file name="protractor.js" type="protractor">
22303                 var user = element(by.exactBinding('user'));
22304                 var userNameValid = element(by.binding('myForm.userName.$valid'));
22305                 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22306                 var lastNameError = element(by.binding('myForm.lastName.$error'));
22307                 var formValid = element(by.binding('myForm.$valid'));
22308                 var userNameInput = element(by.model('user.name'));
22309                 var userLastInput = element(by.model('user.last'));
22310
22311                 it('should initialize to model', function() {
22312                   expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22313                   expect(userNameValid.getText()).toContain('true');
22314                   expect(formValid.getText()).toContain('true');
22315                 });
22316
22317                 it('should be invalid if empty when required', function() {
22318                   userNameInput.clear();
22319                   userNameInput.sendKeys('');
22320
22321                   expect(user.getText()).toContain('{"last":"visitor"}');
22322                   expect(userNameValid.getText()).toContain('false');
22323                   expect(formValid.getText()).toContain('false');
22324                 });
22325
22326                 it('should be valid if empty when min length is set', function() {
22327                   userLastInput.clear();
22328                   userLastInput.sendKeys('');
22329
22330                   expect(user.getText()).toContain('{"name":"guest","last":""}');
22331                   expect(lastNameValid.getText()).toContain('true');
22332                   expect(formValid.getText()).toContain('true');
22333                 });
22334
22335                 it('should be invalid if less than required min length', function() {
22336                   userLastInput.clear();
22337                   userLastInput.sendKeys('xx');
22338
22339                   expect(user.getText()).toContain('{"name":"guest"}');
22340                   expect(lastNameValid.getText()).toContain('false');
22341                   expect(lastNameError.getText()).toContain('minlength');
22342                   expect(formValid.getText()).toContain('false');
22343                 });
22344
22345                 it('should be invalid if longer than max length', function() {
22346                   userLastInput.clear();
22347                   userLastInput.sendKeys('some ridiculously long name');
22348
22349                   expect(user.getText()).toContain('{"name":"guest"}');
22350                   expect(lastNameValid.getText()).toContain('false');
22351                   expect(lastNameError.getText()).toContain('maxlength');
22352                   expect(formValid.getText()).toContain('false');
22353                 });
22354               </file>
22355             </example>
22356          */
22357         var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22358             function($browser, $sniffer, $filter, $parse) {
22359           return {
22360             restrict: 'E',
22361             require: ['?ngModel'],
22362             link: {
22363               pre: function(scope, element, attr, ctrls) {
22364                 if (ctrls[0]) {
22365                   (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22366                                                                       $browser, $filter, $parse);
22367                 }
22368               }
22369             }
22370           };
22371         }];
22372
22373
22374
22375         var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22376         /**
22377          * @ngdoc directive
22378          * @name ngValue
22379          *
22380          * @description
22381          * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22382          * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22383          * the bound value.
22384          *
22385          * `ngValue` is useful when dynamically generating lists of radio buttons using
22386          * {@link ngRepeat `ngRepeat`}, as shown below.
22387          *
22388          * Likewise, `ngValue` can be used to generate `<option>` elements for
22389          * the {@link select `select`} element. In that case however, only strings are supported
22390          * for the `value `attribute, so the resulting `ngModel` will always be a string.
22391          * Support for `select` models with non-string values is available via `ngOptions`.
22392          *
22393          * @element input
22394          * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22395          *   of the `input` element
22396          *
22397          * @example
22398             <example name="ngValue-directive" module="valueExample">
22399               <file name="index.html">
22400                <script>
22401                   angular.module('valueExample', [])
22402                     .controller('ExampleController', ['$scope', function($scope) {
22403                       $scope.names = ['pizza', 'unicorns', 'robots'];
22404                       $scope.my = { favorite: 'unicorns' };
22405                     }]);
22406                </script>
22407                 <form ng-controller="ExampleController">
22408                   <h2>Which is your favorite?</h2>
22409                     <label ng-repeat="name in names" for="{{name}}">
22410                       {{name}}
22411                       <input type="radio"
22412                              ng-model="my.favorite"
22413                              ng-value="name"
22414                              id="{{name}}"
22415                              name="favorite">
22416                     </label>
22417                   <div>You chose {{my.favorite}}</div>
22418                 </form>
22419               </file>
22420               <file name="protractor.js" type="protractor">
22421                 var favorite = element(by.binding('my.favorite'));
22422
22423                 it('should initialize to model', function() {
22424                   expect(favorite.getText()).toContain('unicorns');
22425                 });
22426                 it('should bind the values to the inputs', function() {
22427                   element.all(by.model('my.favorite')).get(0).click();
22428                   expect(favorite.getText()).toContain('pizza');
22429                 });
22430               </file>
22431             </example>
22432          */
22433         var ngValueDirective = function() {
22434           return {
22435             restrict: 'A',
22436             priority: 100,
22437             compile: function(tpl, tplAttr) {
22438               if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22439                 return function ngValueConstantLink(scope, elm, attr) {
22440                   attr.$set('value', scope.$eval(attr.ngValue));
22441                 };
22442               } else {
22443                 return function ngValueLink(scope, elm, attr) {
22444                   scope.$watch(attr.ngValue, function valueWatchAction(value) {
22445                     attr.$set('value', value);
22446                   });
22447                 };
22448               }
22449             }
22450           };
22451         };
22452
22453         /**
22454          * @ngdoc directive
22455          * @name ngBind
22456          * @restrict AC
22457          *
22458          * @description
22459          * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22460          * with the value of a given expression, and to update the text content when the value of that
22461          * expression changes.
22462          *
22463          * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22464          * `{{ expression }}` which is similar but less verbose.
22465          *
22466          * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22467          * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22468          * element attribute, it makes the bindings invisible to the user while the page is loading.
22469          *
22470          * An alternative solution to this problem would be using the
22471          * {@link ng.directive:ngCloak ngCloak} directive.
22472          *
22473          *
22474          * @element ANY
22475          * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22476          *
22477          * @example
22478          * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22479            <example module="bindExample">
22480              <file name="index.html">
22481                <script>
22482                  angular.module('bindExample', [])
22483                    .controller('ExampleController', ['$scope', function($scope) {
22484                      $scope.name = 'Whirled';
22485                    }]);
22486                </script>
22487                <div ng-controller="ExampleController">
22488                  <label>Enter name: <input type="text" ng-model="name"></label><br>
22489                  Hello <span ng-bind="name"></span>!
22490                </div>
22491              </file>
22492              <file name="protractor.js" type="protractor">
22493                it('should check ng-bind', function() {
22494                  var nameInput = element(by.model('name'));
22495
22496                  expect(element(by.binding('name')).getText()).toBe('Whirled');
22497                  nameInput.clear();
22498                  nameInput.sendKeys('world');
22499                  expect(element(by.binding('name')).getText()).toBe('world');
22500                });
22501              </file>
22502            </example>
22503          */
22504         var ngBindDirective = ['$compile', function($compile) {
22505           return {
22506             restrict: 'AC',
22507             compile: function ngBindCompile(templateElement) {
22508               $compile.$$addBindingClass(templateElement);
22509               return function ngBindLink(scope, element, attr) {
22510                 $compile.$$addBindingInfo(element, attr.ngBind);
22511                 element = element[0];
22512                 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22513                   element.textContent = isUndefined(value) ? '' : value;
22514                 });
22515               };
22516             }
22517           };
22518         }];
22519
22520
22521         /**
22522          * @ngdoc directive
22523          * @name ngBindTemplate
22524          *
22525          * @description
22526          * The `ngBindTemplate` directive specifies that the element
22527          * text content should be replaced with the interpolation of the template
22528          * in the `ngBindTemplate` attribute.
22529          * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22530          * expressions. This directive is needed since some HTML elements
22531          * (such as TITLE and OPTION) cannot contain SPAN elements.
22532          *
22533          * @element ANY
22534          * @param {string} ngBindTemplate template of form
22535          *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22536          *
22537          * @example
22538          * Try it here: enter text in text box and watch the greeting change.
22539            <example module="bindExample">
22540              <file name="index.html">
22541                <script>
22542                  angular.module('bindExample', [])
22543                    .controller('ExampleController', ['$scope', function($scope) {
22544                      $scope.salutation = 'Hello';
22545                      $scope.name = 'World';
22546                    }]);
22547                </script>
22548                <div ng-controller="ExampleController">
22549                 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22550                 <label>Name: <input type="text" ng-model="name"></label><br>
22551                 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22552                </div>
22553              </file>
22554              <file name="protractor.js" type="protractor">
22555                it('should check ng-bind', function() {
22556                  var salutationElem = element(by.binding('salutation'));
22557                  var salutationInput = element(by.model('salutation'));
22558                  var nameInput = element(by.model('name'));
22559
22560                  expect(salutationElem.getText()).toBe('Hello World!');
22561
22562                  salutationInput.clear();
22563                  salutationInput.sendKeys('Greetings');
22564                  nameInput.clear();
22565                  nameInput.sendKeys('user');
22566
22567                  expect(salutationElem.getText()).toBe('Greetings user!');
22568                });
22569              </file>
22570            </example>
22571          */
22572         var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22573           return {
22574             compile: function ngBindTemplateCompile(templateElement) {
22575               $compile.$$addBindingClass(templateElement);
22576               return function ngBindTemplateLink(scope, element, attr) {
22577                 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22578                 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22579                 element = element[0];
22580                 attr.$observe('ngBindTemplate', function(value) {
22581                   element.textContent = isUndefined(value) ? '' : value;
22582                 });
22583               };
22584             }
22585           };
22586         }];
22587
22588
22589         /**
22590          * @ngdoc directive
22591          * @name ngBindHtml
22592          *
22593          * @description
22594          * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22595          * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22596          * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22597          * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22598          * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22599          *
22600          * You may also bypass sanitization for values you know are safe. To do so, bind to
22601          * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22602          * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22603          *
22604          * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22605          * will have an exception (instead of an exploit.)
22606          *
22607          * @element ANY
22608          * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22609          *
22610          * @example
22611
22612            <example module="bindHtmlExample" deps="angular-sanitize.js">
22613              <file name="index.html">
22614                <div ng-controller="ExampleController">
22615                 <p ng-bind-html="myHTML"></p>
22616                </div>
22617              </file>
22618
22619              <file name="script.js">
22620                angular.module('bindHtmlExample', ['ngSanitize'])
22621                  .controller('ExampleController', ['$scope', function($scope) {
22622                    $scope.myHTML =
22623                       'I am an <code>HTML</code>string with ' +
22624                       '<a href="#">links!</a> and other <em>stuff</em>';
22625                  }]);
22626              </file>
22627
22628              <file name="protractor.js" type="protractor">
22629                it('should check ng-bind-html', function() {
22630                  expect(element(by.binding('myHTML')).getText()).toBe(
22631                      'I am an HTMLstring with links! and other stuff');
22632                });
22633              </file>
22634            </example>
22635          */
22636         var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22637           return {
22638             restrict: 'A',
22639             compile: function ngBindHtmlCompile(tElement, tAttrs) {
22640               var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22641               var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22642                 return (value || '').toString();
22643               });
22644               $compile.$$addBindingClass(tElement);
22645
22646               return function ngBindHtmlLink(scope, element, attr) {
22647                 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22648
22649                 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22650                   // we re-evaluate the expr because we want a TrustedValueHolderType
22651                   // for $sce, not a string
22652                   element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22653                 });
22654               };
22655             }
22656           };
22657         }];
22658
22659         /**
22660          * @ngdoc directive
22661          * @name ngChange
22662          *
22663          * @description
22664          * Evaluate the given expression when the user changes the input.
22665          * The expression is evaluated immediately, unlike the JavaScript onchange event
22666          * which only triggers at the end of a change (usually, when the user leaves the
22667          * form element or presses the return key).
22668          *
22669          * The `ngChange` expression is only evaluated when a change in the input value causes
22670          * a new value to be committed to the model.
22671          *
22672          * It will not be evaluated:
22673          * * if the value returned from the `$parsers` transformation pipeline has not changed
22674          * * if the input has continued to be invalid since the model will stay `null`
22675          * * if the model is changed programmatically and not by a change to the input value
22676          *
22677          *
22678          * Note, this directive requires `ngModel` to be present.
22679          *
22680          * @element input
22681          * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22682          * in input value.
22683          *
22684          * @example
22685          * <example name="ngChange-directive" module="changeExample">
22686          *   <file name="index.html">
22687          *     <script>
22688          *       angular.module('changeExample', [])
22689          *         .controller('ExampleController', ['$scope', function($scope) {
22690          *           $scope.counter = 0;
22691          *           $scope.change = function() {
22692          *             $scope.counter++;
22693          *           };
22694          *         }]);
22695          *     </script>
22696          *     <div ng-controller="ExampleController">
22697          *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22698          *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22699          *       <label for="ng-change-example2">Confirmed</label><br />
22700          *       <tt>debug = {{confirmed}}</tt><br/>
22701          *       <tt>counter = {{counter}}</tt><br/>
22702          *     </div>
22703          *   </file>
22704          *   <file name="protractor.js" type="protractor">
22705          *     var counter = element(by.binding('counter'));
22706          *     var debug = element(by.binding('confirmed'));
22707          *
22708          *     it('should evaluate the expression if changing from view', function() {
22709          *       expect(counter.getText()).toContain('0');
22710          *
22711          *       element(by.id('ng-change-example1')).click();
22712          *
22713          *       expect(counter.getText()).toContain('1');
22714          *       expect(debug.getText()).toContain('true');
22715          *     });
22716          *
22717          *     it('should not evaluate the expression if changing from model', function() {
22718          *       element(by.id('ng-change-example2')).click();
22719
22720          *       expect(counter.getText()).toContain('0');
22721          *       expect(debug.getText()).toContain('true');
22722          *     });
22723          *   </file>
22724          * </example>
22725          */
22726         var ngChangeDirective = valueFn({
22727           restrict: 'A',
22728           require: 'ngModel',
22729           link: function(scope, element, attr, ctrl) {
22730             ctrl.$viewChangeListeners.push(function() {
22731               scope.$eval(attr.ngChange);
22732             });
22733           }
22734         });
22735
22736         function classDirective(name, selector) {
22737           name = 'ngClass' + name;
22738           return ['$animate', function($animate) {
22739             return {
22740               restrict: 'AC',
22741               link: function(scope, element, attr) {
22742                 var oldVal;
22743
22744                 scope.$watch(attr[name], ngClassWatchAction, true);
22745
22746                 attr.$observe('class', function(value) {
22747                   ngClassWatchAction(scope.$eval(attr[name]));
22748                 });
22749
22750
22751                 if (name !== 'ngClass') {
22752                   scope.$watch('$index', function($index, old$index) {
22753                     // jshint bitwise: false
22754                     var mod = $index & 1;
22755                     if (mod !== (old$index & 1)) {
22756                       var classes = arrayClasses(scope.$eval(attr[name]));
22757                       mod === selector ?
22758                         addClasses(classes) :
22759                         removeClasses(classes);
22760                     }
22761                   });
22762                 }
22763
22764                 function addClasses(classes) {
22765                   var newClasses = digestClassCounts(classes, 1);
22766                   attr.$addClass(newClasses);
22767                 }
22768
22769                 function removeClasses(classes) {
22770                   var newClasses = digestClassCounts(classes, -1);
22771                   attr.$removeClass(newClasses);
22772                 }
22773
22774                 function digestClassCounts(classes, count) {
22775                   // Use createMap() to prevent class assumptions involving property
22776                   // names in Object.prototype
22777                   var classCounts = element.data('$classCounts') || createMap();
22778                   var classesToUpdate = [];
22779                   forEach(classes, function(className) {
22780                     if (count > 0 || classCounts[className]) {
22781                       classCounts[className] = (classCounts[className] || 0) + count;
22782                       if (classCounts[className] === +(count > 0)) {
22783                         classesToUpdate.push(className);
22784                       }
22785                     }
22786                   });
22787                   element.data('$classCounts', classCounts);
22788                   return classesToUpdate.join(' ');
22789                 }
22790
22791                 function updateClasses(oldClasses, newClasses) {
22792                   var toAdd = arrayDifference(newClasses, oldClasses);
22793                   var toRemove = arrayDifference(oldClasses, newClasses);
22794                   toAdd = digestClassCounts(toAdd, 1);
22795                   toRemove = digestClassCounts(toRemove, -1);
22796                   if (toAdd && toAdd.length) {
22797                     $animate.addClass(element, toAdd);
22798                   }
22799                   if (toRemove && toRemove.length) {
22800                     $animate.removeClass(element, toRemove);
22801                   }
22802                 }
22803
22804                 function ngClassWatchAction(newVal) {
22805                   if (selector === true || scope.$index % 2 === selector) {
22806                     var newClasses = arrayClasses(newVal || []);
22807                     if (!oldVal) {
22808                       addClasses(newClasses);
22809                     } else if (!equals(newVal,oldVal)) {
22810                       var oldClasses = arrayClasses(oldVal);
22811                       updateClasses(oldClasses, newClasses);
22812                     }
22813                   }
22814                   oldVal = shallowCopy(newVal);
22815                 }
22816               }
22817             };
22818
22819             function arrayDifference(tokens1, tokens2) {
22820               var values = [];
22821
22822               outer:
22823               for (var i = 0; i < tokens1.length; i++) {
22824                 var token = tokens1[i];
22825                 for (var j = 0; j < tokens2.length; j++) {
22826                   if (token == tokens2[j]) continue outer;
22827                 }
22828                 values.push(token);
22829               }
22830               return values;
22831             }
22832
22833             function arrayClasses(classVal) {
22834               var classes = [];
22835               if (isArray(classVal)) {
22836                 forEach(classVal, function(v) {
22837                   classes = classes.concat(arrayClasses(v));
22838                 });
22839                 return classes;
22840               } else if (isString(classVal)) {
22841                 return classVal.split(' ');
22842               } else if (isObject(classVal)) {
22843                 forEach(classVal, function(v, k) {
22844                   if (v) {
22845                     classes = classes.concat(k.split(' '));
22846                   }
22847                 });
22848                 return classes;
22849               }
22850               return classVal;
22851             }
22852           }];
22853         }
22854
22855         /**
22856          * @ngdoc directive
22857          * @name ngClass
22858          * @restrict AC
22859          *
22860          * @description
22861          * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22862          * an expression that represents all classes to be added.
22863          *
22864          * The directive operates in three different ways, depending on which of three types the expression
22865          * evaluates to:
22866          *
22867          * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22868          * names.
22869          *
22870          * 2. If the expression evaluates to an object, then for each key-value pair of the
22871          * object with a truthy value the corresponding key is used as a class name.
22872          *
22873          * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22874          * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22875          * to give you more control over what CSS classes appear. See the code below for an example of this.
22876          *
22877          *
22878          * The directive won't add duplicate classes if a particular class was already set.
22879          *
22880          * When the expression changes, the previously added classes are removed and only then are the
22881          * new classes added.
22882          *
22883          * @animations
22884          * **add** - happens just before the class is applied to the elements
22885          *
22886          * **remove** - happens just before the class is removed from the element
22887          *
22888          * @element ANY
22889          * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22890          *   of the evaluation can be a string representing space delimited class
22891          *   names, an array, or a map of class names to boolean values. In the case of a map, the
22892          *   names of the properties whose values are truthy will be added as css classes to the
22893          *   element.
22894          *
22895          * @example Example that demonstrates basic bindings via ngClass directive.
22896            <example>
22897              <file name="index.html">
22898                <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22899                <label>
22900                   <input type="checkbox" ng-model="deleted">
22901                   deleted (apply "strike" class)
22902                </label><br>
22903                <label>
22904                   <input type="checkbox" ng-model="important">
22905                   important (apply "bold" class)
22906                </label><br>
22907                <label>
22908                   <input type="checkbox" ng-model="error">
22909                   error (apply "has-error" class)
22910                </label>
22911                <hr>
22912                <p ng-class="style">Using String Syntax</p>
22913                <input type="text" ng-model="style"
22914                       placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22915                <hr>
22916                <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22917                <input ng-model="style1"
22918                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22919                <input ng-model="style2"
22920                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22921                <input ng-model="style3"
22922                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22923                <hr>
22924                <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22925                <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22926                <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22927              </file>
22928              <file name="style.css">
22929                .strike {
22930                    text-decoration: line-through;
22931                }
22932                .bold {
22933                    font-weight: bold;
22934                }
22935                .red {
22936                    color: red;
22937                }
22938                .has-error {
22939                    color: red;
22940                    background-color: yellow;
22941                }
22942                .orange {
22943                    color: orange;
22944                }
22945              </file>
22946              <file name="protractor.js" type="protractor">
22947                var ps = element.all(by.css('p'));
22948
22949                it('should let you toggle the class', function() {
22950
22951                  expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22952                  expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22953
22954                  element(by.model('important')).click();
22955                  expect(ps.first().getAttribute('class')).toMatch(/bold/);
22956
22957                  element(by.model('error')).click();
22958                  expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22959                });
22960
22961                it('should let you toggle string example', function() {
22962                  expect(ps.get(1).getAttribute('class')).toBe('');
22963                  element(by.model('style')).clear();
22964                  element(by.model('style')).sendKeys('red');
22965                  expect(ps.get(1).getAttribute('class')).toBe('red');
22966                });
22967
22968                it('array example should have 3 classes', function() {
22969                  expect(ps.get(2).getAttribute('class')).toBe('');
22970                  element(by.model('style1')).sendKeys('bold');
22971                  element(by.model('style2')).sendKeys('strike');
22972                  element(by.model('style3')).sendKeys('red');
22973                  expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22974                });
22975
22976                it('array with map example should have 2 classes', function() {
22977                  expect(ps.last().getAttribute('class')).toBe('');
22978                  element(by.model('style4')).sendKeys('bold');
22979                  element(by.model('warning')).click();
22980                  expect(ps.last().getAttribute('class')).toBe('bold orange');
22981                });
22982              </file>
22983            </example>
22984
22985            ## Animations
22986
22987            The example below demonstrates how to perform animations using ngClass.
22988
22989            <example module="ngAnimate" deps="angular-animate.js" animations="true">
22990              <file name="index.html">
22991               <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22992               <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22993               <br>
22994               <span class="base-class" ng-class="myVar">Sample Text</span>
22995              </file>
22996              <file name="style.css">
22997                .base-class {
22998                  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22999                }
23000
23001                .base-class.my-class {
23002                  color: red;
23003                  font-size:3em;
23004                }
23005              </file>
23006              <file name="protractor.js" type="protractor">
23007                it('should check ng-class', function() {
23008                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23009                    toMatch(/my-class/);
23010
23011                  element(by.id('setbtn')).click();
23012
23013                  expect(element(by.css('.base-class')).getAttribute('class')).
23014                    toMatch(/my-class/);
23015
23016                  element(by.id('clearbtn')).click();
23017
23018                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23019                    toMatch(/my-class/);
23020                });
23021              </file>
23022            </example>
23023
23024
23025            ## ngClass and pre-existing CSS3 Transitions/Animations
23026            The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23027            Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23028            any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23029            to view the step by step details of {@link $animate#addClass $animate.addClass} and
23030            {@link $animate#removeClass $animate.removeClass}.
23031          */
23032         var ngClassDirective = classDirective('', true);
23033
23034         /**
23035          * @ngdoc directive
23036          * @name ngClassOdd
23037          * @restrict AC
23038          *
23039          * @description
23040          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23041          * {@link ng.directive:ngClass ngClass}, except they work in
23042          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23043          *
23044          * This directive can be applied only within the scope of an
23045          * {@link ng.directive:ngRepeat ngRepeat}.
23046          *
23047          * @element ANY
23048          * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23049          *   of the evaluation can be a string representing space delimited class names or an array.
23050          *
23051          * @example
23052            <example>
23053              <file name="index.html">
23054                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23055                   <li ng-repeat="name in names">
23056                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23057                      {{name}}
23058                    </span>
23059                   </li>
23060                 </ol>
23061              </file>
23062              <file name="style.css">
23063                .odd {
23064                  color: red;
23065                }
23066                .even {
23067                  color: blue;
23068                }
23069              </file>
23070              <file name="protractor.js" type="protractor">
23071                it('should check ng-class-odd and ng-class-even', function() {
23072                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23073                    toMatch(/odd/);
23074                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23075                    toMatch(/even/);
23076                });
23077              </file>
23078            </example>
23079          */
23080         var ngClassOddDirective = classDirective('Odd', 0);
23081
23082         /**
23083          * @ngdoc directive
23084          * @name ngClassEven
23085          * @restrict AC
23086          *
23087          * @description
23088          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23089          * {@link ng.directive:ngClass ngClass}, except they work in
23090          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23091          *
23092          * This directive can be applied only within the scope of an
23093          * {@link ng.directive:ngRepeat ngRepeat}.
23094          *
23095          * @element ANY
23096          * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23097          *   result of the evaluation can be a string representing space delimited class names or an array.
23098          *
23099          * @example
23100            <example>
23101              <file name="index.html">
23102                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23103                   <li ng-repeat="name in names">
23104                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23105                      {{name}} &nbsp; &nbsp; &nbsp;
23106                    </span>
23107                   </li>
23108                 </ol>
23109              </file>
23110              <file name="style.css">
23111                .odd {
23112                  color: red;
23113                }
23114                .even {
23115                  color: blue;
23116                }
23117              </file>
23118              <file name="protractor.js" type="protractor">
23119                it('should check ng-class-odd and ng-class-even', function() {
23120                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23121                    toMatch(/odd/);
23122                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23123                    toMatch(/even/);
23124                });
23125              </file>
23126            </example>
23127          */
23128         var ngClassEvenDirective = classDirective('Even', 1);
23129
23130         /**
23131          * @ngdoc directive
23132          * @name ngCloak
23133          * @restrict AC
23134          *
23135          * @description
23136          * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23137          * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23138          * directive to avoid the undesirable flicker effect caused by the html template display.
23139          *
23140          * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23141          * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23142          * of the browser view.
23143          *
23144          * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23145          * `angular.min.js`.
23146          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23147          *
23148          * ```css
23149          * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23150          *   display: none !important;
23151          * }
23152          * ```
23153          *
23154          * When this css rule is loaded by the browser, all html elements (including their children) that
23155          * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23156          * during the compilation of the template it deletes the `ngCloak` element attribute, making
23157          * the compiled element visible.
23158          *
23159          * For the best result, the `angular.js` script must be loaded in the head section of the html
23160          * document; alternatively, the css rule above must be included in the external stylesheet of the
23161          * application.
23162          *
23163          * @element ANY
23164          *
23165          * @example
23166            <example>
23167              <file name="index.html">
23168                 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23169                 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23170              </file>
23171              <file name="protractor.js" type="protractor">
23172                it('should remove the template directive and css class', function() {
23173                  expect($('#template1').getAttribute('ng-cloak')).
23174                    toBeNull();
23175                  expect($('#template2').getAttribute('ng-cloak')).
23176                    toBeNull();
23177                });
23178              </file>
23179            </example>
23180          *
23181          */
23182         var ngCloakDirective = ngDirective({
23183           compile: function(element, attr) {
23184             attr.$set('ngCloak', undefined);
23185             element.removeClass('ng-cloak');
23186           }
23187         });
23188
23189         /**
23190          * @ngdoc directive
23191          * @name ngController
23192          *
23193          * @description
23194          * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23195          * supports the principles behind the Model-View-Controller design pattern.
23196          *
23197          * MVC components in angular:
23198          *
23199          * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23200          *   are accessed through bindings.
23201          * * View — The template (HTML with data bindings) that is rendered into the View.
23202          * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23203          *   logic behind the application to decorate the scope with functions and values
23204          *
23205          * Note that you can also attach controllers to the DOM by declaring it in a route definition
23206          * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23207          * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23208          * and executed twice.
23209          *
23210          * @element ANY
23211          * @scope
23212          * @priority 500
23213          * @param {expression} ngController Name of a constructor function registered with the current
23214          * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23215          * that on the current scope evaluates to a constructor function.
23216          *
23217          * The controller instance can be published into a scope property by specifying
23218          * `ng-controller="as propertyName"`.
23219          *
23220          * If the current `$controllerProvider` is configured to use globals (via
23221          * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23222          * also be the name of a globally accessible constructor function (not recommended).
23223          *
23224          * @example
23225          * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23226          * greeting are methods declared on the controller (see source tab). These methods can
23227          * easily be called from the angular markup. Any changes to the data are automatically reflected
23228          * in the View without the need for a manual update.
23229          *
23230          * Two different declaration styles are included below:
23231          *
23232          * * one binds methods and properties directly onto the controller using `this`:
23233          * `ng-controller="SettingsController1 as settings"`
23234          * * one injects `$scope` into the controller:
23235          * `ng-controller="SettingsController2"`
23236          *
23237          * The second option is more common in the Angular community, and is generally used in boilerplates
23238          * and in this guide. However, there are advantages to binding properties directly to the controller
23239          * and avoiding scope.
23240          *
23241          * * Using `controller as` makes it obvious which controller you are accessing in the template when
23242          * multiple controllers apply to an element.
23243          * * If you are writing your controllers as classes you have easier access to the properties and
23244          * methods, which will appear on the scope, from inside the controller code.
23245          * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23246          * inheritance masking primitives.
23247          *
23248          * This example demonstrates the `controller as` syntax.
23249          *
23250          * <example name="ngControllerAs" module="controllerAsExample">
23251          *   <file name="index.html">
23252          *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23253          *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23254          *      <button ng-click="settings.greet()">greet</button><br/>
23255          *      Contact:
23256          *      <ul>
23257          *        <li ng-repeat="contact in settings.contacts">
23258          *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23259          *             <option>phone</option>
23260          *             <option>email</option>
23261          *          </select>
23262          *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23263          *          <button ng-click="settings.clearContact(contact)">clear</button>
23264          *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23265          *        </li>
23266          *        <li><button ng-click="settings.addContact()">add</button></li>
23267          *     </ul>
23268          *    </div>
23269          *   </file>
23270          *   <file name="app.js">
23271          *    angular.module('controllerAsExample', [])
23272          *      .controller('SettingsController1', SettingsController1);
23273          *
23274          *    function SettingsController1() {
23275          *      this.name = "John Smith";
23276          *      this.contacts = [
23277          *        {type: 'phone', value: '408 555 1212'},
23278          *        {type: 'email', value: 'john.smith@example.org'} ];
23279          *    }
23280          *
23281          *    SettingsController1.prototype.greet = function() {
23282          *      alert(this.name);
23283          *    };
23284          *
23285          *    SettingsController1.prototype.addContact = function() {
23286          *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
23287          *    };
23288          *
23289          *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23290          *     var index = this.contacts.indexOf(contactToRemove);
23291          *      this.contacts.splice(index, 1);
23292          *    };
23293          *
23294          *    SettingsController1.prototype.clearContact = function(contact) {
23295          *      contact.type = 'phone';
23296          *      contact.value = '';
23297          *    };
23298          *   </file>
23299          *   <file name="protractor.js" type="protractor">
23300          *     it('should check controller as', function() {
23301          *       var container = element(by.id('ctrl-as-exmpl'));
23302          *         expect(container.element(by.model('settings.name'))
23303          *           .getAttribute('value')).toBe('John Smith');
23304          *
23305          *       var firstRepeat =
23306          *           container.element(by.repeater('contact in settings.contacts').row(0));
23307          *       var secondRepeat =
23308          *           container.element(by.repeater('contact in settings.contacts').row(1));
23309          *
23310          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23311          *           .toBe('408 555 1212');
23312          *
23313          *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23314          *           .toBe('john.smith@example.org');
23315          *
23316          *       firstRepeat.element(by.buttonText('clear')).click();
23317          *
23318          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23319          *           .toBe('');
23320          *
23321          *       container.element(by.buttonText('add')).click();
23322          *
23323          *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23324          *           .element(by.model('contact.value'))
23325          *           .getAttribute('value'))
23326          *           .toBe('yourname@example.org');
23327          *     });
23328          *   </file>
23329          * </example>
23330          *
23331          * This example demonstrates the "attach to `$scope`" style of controller.
23332          *
23333          * <example name="ngController" module="controllerExample">
23334          *  <file name="index.html">
23335          *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23336          *     <label>Name: <input type="text" ng-model="name"/></label>
23337          *     <button ng-click="greet()">greet</button><br/>
23338          *     Contact:
23339          *     <ul>
23340          *       <li ng-repeat="contact in contacts">
23341          *         <select ng-model="contact.type" id="select_{{$index}}">
23342          *            <option>phone</option>
23343          *            <option>email</option>
23344          *         </select>
23345          *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23346          *         <button ng-click="clearContact(contact)">clear</button>
23347          *         <button ng-click="removeContact(contact)">X</button>
23348          *       </li>
23349          *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23350          *    </ul>
23351          *   </div>
23352          *  </file>
23353          *  <file name="app.js">
23354          *   angular.module('controllerExample', [])
23355          *     .controller('SettingsController2', ['$scope', SettingsController2]);
23356          *
23357          *   function SettingsController2($scope) {
23358          *     $scope.name = "John Smith";
23359          *     $scope.contacts = [
23360          *       {type:'phone', value:'408 555 1212'},
23361          *       {type:'email', value:'john.smith@example.org'} ];
23362          *
23363          *     $scope.greet = function() {
23364          *       alert($scope.name);
23365          *     };
23366          *
23367          *     $scope.addContact = function() {
23368          *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
23369          *     };
23370          *
23371          *     $scope.removeContact = function(contactToRemove) {
23372          *       var index = $scope.contacts.indexOf(contactToRemove);
23373          *       $scope.contacts.splice(index, 1);
23374          *     };
23375          *
23376          *     $scope.clearContact = function(contact) {
23377          *       contact.type = 'phone';
23378          *       contact.value = '';
23379          *     };
23380          *   }
23381          *  </file>
23382          *  <file name="protractor.js" type="protractor">
23383          *    it('should check controller', function() {
23384          *      var container = element(by.id('ctrl-exmpl'));
23385          *
23386          *      expect(container.element(by.model('name'))
23387          *          .getAttribute('value')).toBe('John Smith');
23388          *
23389          *      var firstRepeat =
23390          *          container.element(by.repeater('contact in contacts').row(0));
23391          *      var secondRepeat =
23392          *          container.element(by.repeater('contact in contacts').row(1));
23393          *
23394          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23395          *          .toBe('408 555 1212');
23396          *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23397          *          .toBe('john.smith@example.org');
23398          *
23399          *      firstRepeat.element(by.buttonText('clear')).click();
23400          *
23401          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23402          *          .toBe('');
23403          *
23404          *      container.element(by.buttonText('add')).click();
23405          *
23406          *      expect(container.element(by.repeater('contact in contacts').row(2))
23407          *          .element(by.model('contact.value'))
23408          *          .getAttribute('value'))
23409          *          .toBe('yourname@example.org');
23410          *    });
23411          *  </file>
23412          *</example>
23413
23414          */
23415         var ngControllerDirective = [function() {
23416           return {
23417             restrict: 'A',
23418             scope: true,
23419             controller: '@',
23420             priority: 500
23421           };
23422         }];
23423
23424         /**
23425          * @ngdoc directive
23426          * @name ngCsp
23427          *
23428          * @element html
23429          * @description
23430          *
23431          * Angular has some features that can break certain
23432          * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23433          *
23434          * If you intend to implement these rules then you must tell Angular not to use these features.
23435          *
23436          * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23437          *
23438          *
23439          * The following rules affect Angular:
23440          *
23441          * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23442          * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23443          * increase in the speed of evaluating Angular expressions.
23444          *
23445          * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23446          * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23447          * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23448          * `angular-csp.css` in your HTML manually.
23449          *
23450          * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23451          * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23452          * however, triggers a CSP error to be logged in the console:
23453          *
23454          * ```
23455          * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23456          * script in the following Content Security Policy directive: "default-src 'self'". Note that
23457          * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23458          * ```
23459          *
23460          * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23461          * directive on an element of the HTML document that appears before the `<script>` tag that loads
23462          * the `angular.js` file.
23463          *
23464          * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23465          *
23466          * You can specify which of the CSP related Angular features should be deactivated by providing
23467          * a value for the `ng-csp` attribute. The options are as follows:
23468          *
23469          * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23470          *
23471          * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23472          *
23473          * You can use these values in the following combinations:
23474          *
23475          *
23476          * * No declaration means that Angular will assume that you can do inline styles, but it will do
23477          * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23478          * of Angular.
23479          *
23480          * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23481          * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23482          * of Angular.
23483          *
23484          * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23485          * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23486          *
23487          * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23488          * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23489          *
23490          * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23491          * styles nor use eval, which is the same as an empty: ng-csp.
23492          * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23493          *
23494          * @example
23495          * This example shows how to apply the `ngCsp` directive to the `html` tag.
23496            ```html
23497              <!doctype html>
23498              <html ng-app ng-csp>
23499              ...
23500              ...
23501              </html>
23502            ```
23503           * @example
23504               // Note: the suffix `.csp` in the example name triggers
23505               // csp mode in our http server!
23506               <example name="example.csp" module="cspExample" ng-csp="true">
23507                 <file name="index.html">
23508                   <div ng-controller="MainController as ctrl">
23509                     <div>
23510                       <button ng-click="ctrl.inc()" id="inc">Increment</button>
23511                       <span id="counter">
23512                         {{ctrl.counter}}
23513                       </span>
23514                     </div>
23515
23516                     <div>
23517                       <button ng-click="ctrl.evil()" id="evil">Evil</button>
23518                       <span id="evilError">
23519                         {{ctrl.evilError}}
23520                       </span>
23521                     </div>
23522                   </div>
23523                 </file>
23524                 <file name="script.js">
23525                    angular.module('cspExample', [])
23526                      .controller('MainController', function() {
23527                         this.counter = 0;
23528                         this.inc = function() {
23529                           this.counter++;
23530                         };
23531                         this.evil = function() {
23532                           // jshint evil:true
23533                           try {
23534                             eval('1+2');
23535                           } catch (e) {
23536                             this.evilError = e.message;
23537                           }
23538                         };
23539                       });
23540                 </file>
23541                 <file name="protractor.js" type="protractor">
23542                   var util, webdriver;
23543
23544                   var incBtn = element(by.id('inc'));
23545                   var counter = element(by.id('counter'));
23546                   var evilBtn = element(by.id('evil'));
23547                   var evilError = element(by.id('evilError'));
23548
23549                   function getAndClearSevereErrors() {
23550                     return browser.manage().logs().get('browser').then(function(browserLog) {
23551                       return browserLog.filter(function(logEntry) {
23552                         return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23553                       });
23554                     });
23555                   }
23556
23557                   function clearErrors() {
23558                     getAndClearSevereErrors();
23559                   }
23560
23561                   function expectNoErrors() {
23562                     getAndClearSevereErrors().then(function(filteredLog) {
23563                       expect(filteredLog.length).toEqual(0);
23564                       if (filteredLog.length) {
23565                         console.log('browser console errors: ' + util.inspect(filteredLog));
23566                       }
23567                     });
23568                   }
23569
23570                   function expectError(regex) {
23571                     getAndClearSevereErrors().then(function(filteredLog) {
23572                       var found = false;
23573                       filteredLog.forEach(function(log) {
23574                         if (log.message.match(regex)) {
23575                           found = true;
23576                         }
23577                       });
23578                       if (!found) {
23579                         throw new Error('expected an error that matches ' + regex);
23580                       }
23581                     });
23582                   }
23583
23584                   beforeEach(function() {
23585                     util = require('util');
23586                     webdriver = require('protractor/node_modules/selenium-webdriver');
23587                   });
23588
23589                   // For now, we only test on Chrome,
23590                   // as Safari does not load the page with Protractor's injected scripts,
23591                   // and Firefox webdriver always disables content security policy (#6358)
23592                   if (browser.params.browser !== 'chrome') {
23593                     return;
23594                   }
23595
23596                   it('should not report errors when the page is loaded', function() {
23597                     // clear errors so we are not dependent on previous tests
23598                     clearErrors();
23599                     // Need to reload the page as the page is already loaded when
23600                     // we come here
23601                     browser.driver.getCurrentUrl().then(function(url) {
23602                       browser.get(url);
23603                     });
23604                     expectNoErrors();
23605                   });
23606
23607                   it('should evaluate expressions', function() {
23608                     expect(counter.getText()).toEqual('0');
23609                     incBtn.click();
23610                     expect(counter.getText()).toEqual('1');
23611                     expectNoErrors();
23612                   });
23613
23614                   it('should throw and report an error when using "eval"', function() {
23615                     evilBtn.click();
23616                     expect(evilError.getText()).toMatch(/Content Security Policy/);
23617                     expectError(/Content Security Policy/);
23618                   });
23619                 </file>
23620               </example>
23621           */
23622
23623         // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23624         // bootstrap the system (before $parse is instantiated), for this reason we just have
23625         // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23626
23627         /**
23628          * @ngdoc directive
23629          * @name ngClick
23630          *
23631          * @description
23632          * The ngClick directive allows you to specify custom behavior when
23633          * an element is clicked.
23634          *
23635          * @element ANY
23636          * @priority 0
23637          * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23638          * click. ({@link guide/expression#-event- Event object is available as `$event`})
23639          *
23640          * @example
23641            <example>
23642              <file name="index.html">
23643               <button ng-click="count = count + 1" ng-init="count=0">
23644                 Increment
23645               </button>
23646               <span>
23647                 count: {{count}}
23648               </span>
23649              </file>
23650              <file name="protractor.js" type="protractor">
23651                it('should check ng-click', function() {
23652                  expect(element(by.binding('count')).getText()).toMatch('0');
23653                  element(by.css('button')).click();
23654                  expect(element(by.binding('count')).getText()).toMatch('1');
23655                });
23656              </file>
23657            </example>
23658          */
23659         /*
23660          * A collection of directives that allows creation of custom event handlers that are defined as
23661          * angular expressions and are compiled and executed within the current scope.
23662          */
23663         var ngEventDirectives = {};
23664
23665         // For events that might fire synchronously during DOM manipulation
23666         // we need to execute their event handlers asynchronously using $evalAsync,
23667         // so that they are not executed in an inconsistent state.
23668         var forceAsyncEvents = {
23669           'blur': true,
23670           'focus': true
23671         };
23672         forEach(
23673           'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23674           function(eventName) {
23675             var directiveName = directiveNormalize('ng-' + eventName);
23676             ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23677               return {
23678                 restrict: 'A',
23679                 compile: function($element, attr) {
23680                   // We expose the powerful $event object on the scope that provides access to the Window,
23681                   // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23682                   // checks at the cost of speed since event handler expressions are not executed as
23683                   // frequently as regular change detection.
23684                   var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23685                   return function ngEventHandler(scope, element) {
23686                     element.on(eventName, function(event) {
23687                       var callback = function() {
23688                         fn(scope, {$event:event});
23689                       };
23690                       if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23691                         scope.$evalAsync(callback);
23692                       } else {
23693                         scope.$apply(callback);
23694                       }
23695                     });
23696                   };
23697                 }
23698               };
23699             }];
23700           }
23701         );
23702
23703         /**
23704          * @ngdoc directive
23705          * @name ngDblclick
23706          *
23707          * @description
23708          * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23709          *
23710          * @element ANY
23711          * @priority 0
23712          * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23713          * a dblclick. (The Event object is available as `$event`)
23714          *
23715          * @example
23716            <example>
23717              <file name="index.html">
23718               <button ng-dblclick="count = count + 1" ng-init="count=0">
23719                 Increment (on double click)
23720               </button>
23721               count: {{count}}
23722              </file>
23723            </example>
23724          */
23725
23726
23727         /**
23728          * @ngdoc directive
23729          * @name ngMousedown
23730          *
23731          * @description
23732          * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23733          *
23734          * @element ANY
23735          * @priority 0
23736          * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23737          * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23738          *
23739          * @example
23740            <example>
23741              <file name="index.html">
23742               <button ng-mousedown="count = count + 1" ng-init="count=0">
23743                 Increment (on mouse down)
23744               </button>
23745               count: {{count}}
23746              </file>
23747            </example>
23748          */
23749
23750
23751         /**
23752          * @ngdoc directive
23753          * @name ngMouseup
23754          *
23755          * @description
23756          * Specify custom behavior on mouseup event.
23757          *
23758          * @element ANY
23759          * @priority 0
23760          * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23761          * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23762          *
23763          * @example
23764            <example>
23765              <file name="index.html">
23766               <button ng-mouseup="count = count + 1" ng-init="count=0">
23767                 Increment (on mouse up)
23768               </button>
23769               count: {{count}}
23770              </file>
23771            </example>
23772          */
23773
23774         /**
23775          * @ngdoc directive
23776          * @name ngMouseover
23777          *
23778          * @description
23779          * Specify custom behavior on mouseover event.
23780          *
23781          * @element ANY
23782          * @priority 0
23783          * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23784          * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23785          *
23786          * @example
23787            <example>
23788              <file name="index.html">
23789               <button ng-mouseover="count = count + 1" ng-init="count=0">
23790                 Increment (when mouse is over)
23791               </button>
23792               count: {{count}}
23793              </file>
23794            </example>
23795          */
23796
23797
23798         /**
23799          * @ngdoc directive
23800          * @name ngMouseenter
23801          *
23802          * @description
23803          * Specify custom behavior on mouseenter event.
23804          *
23805          * @element ANY
23806          * @priority 0
23807          * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23808          * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23809          *
23810          * @example
23811            <example>
23812              <file name="index.html">
23813               <button ng-mouseenter="count = count + 1" ng-init="count=0">
23814                 Increment (when mouse enters)
23815               </button>
23816               count: {{count}}
23817              </file>
23818            </example>
23819          */
23820
23821
23822         /**
23823          * @ngdoc directive
23824          * @name ngMouseleave
23825          *
23826          * @description
23827          * Specify custom behavior on mouseleave event.
23828          *
23829          * @element ANY
23830          * @priority 0
23831          * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23832          * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23833          *
23834          * @example
23835            <example>
23836              <file name="index.html">
23837               <button ng-mouseleave="count = count + 1" ng-init="count=0">
23838                 Increment (when mouse leaves)
23839               </button>
23840               count: {{count}}
23841              </file>
23842            </example>
23843          */
23844
23845
23846         /**
23847          * @ngdoc directive
23848          * @name ngMousemove
23849          *
23850          * @description
23851          * Specify custom behavior on mousemove event.
23852          *
23853          * @element ANY
23854          * @priority 0
23855          * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23856          * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23857          *
23858          * @example
23859            <example>
23860              <file name="index.html">
23861               <button ng-mousemove="count = count + 1" ng-init="count=0">
23862                 Increment (when mouse moves)
23863               </button>
23864               count: {{count}}
23865              </file>
23866            </example>
23867          */
23868
23869
23870         /**
23871          * @ngdoc directive
23872          * @name ngKeydown
23873          *
23874          * @description
23875          * Specify custom behavior on keydown event.
23876          *
23877          * @element ANY
23878          * @priority 0
23879          * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23880          * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23881          *
23882          * @example
23883            <example>
23884              <file name="index.html">
23885               <input ng-keydown="count = count + 1" ng-init="count=0">
23886               key down count: {{count}}
23887              </file>
23888            </example>
23889          */
23890
23891
23892         /**
23893          * @ngdoc directive
23894          * @name ngKeyup
23895          *
23896          * @description
23897          * Specify custom behavior on keyup event.
23898          *
23899          * @element ANY
23900          * @priority 0
23901          * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23902          * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23903          *
23904          * @example
23905            <example>
23906              <file name="index.html">
23907                <p>Typing in the input box below updates the key count</p>
23908                <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23909
23910                <p>Typing in the input box below updates the keycode</p>
23911                <input ng-keyup="event=$event">
23912                <p>event keyCode: {{ event.keyCode }}</p>
23913                <p>event altKey: {{ event.altKey }}</p>
23914              </file>
23915            </example>
23916          */
23917
23918
23919         /**
23920          * @ngdoc directive
23921          * @name ngKeypress
23922          *
23923          * @description
23924          * Specify custom behavior on keypress event.
23925          *
23926          * @element ANY
23927          * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23928          * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23929          * and can be interrogated for keyCode, altKey, etc.)
23930          *
23931          * @example
23932            <example>
23933              <file name="index.html">
23934               <input ng-keypress="count = count + 1" ng-init="count=0">
23935               key press count: {{count}}
23936              </file>
23937            </example>
23938          */
23939
23940
23941         /**
23942          * @ngdoc directive
23943          * @name ngSubmit
23944          *
23945          * @description
23946          * Enables binding angular expressions to onsubmit events.
23947          *
23948          * Additionally it prevents the default action (which for form means sending the request to the
23949          * server and reloading the current page), but only if the form does not contain `action`,
23950          * `data-action`, or `x-action` attributes.
23951          *
23952          * <div class="alert alert-warning">
23953          * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23954          * `ngSubmit` handlers together. See the
23955          * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23956          * for a detailed discussion of when `ngSubmit` may be triggered.
23957          * </div>
23958          *
23959          * @element form
23960          * @priority 0
23961          * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23962          * ({@link guide/expression#-event- Event object is available as `$event`})
23963          *
23964          * @example
23965            <example module="submitExample">
23966              <file name="index.html">
23967               <script>
23968                 angular.module('submitExample', [])
23969                   .controller('ExampleController', ['$scope', function($scope) {
23970                     $scope.list = [];
23971                     $scope.text = 'hello';
23972                     $scope.submit = function() {
23973                       if ($scope.text) {
23974                         $scope.list.push(this.text);
23975                         $scope.text = '';
23976                       }
23977                     };
23978                   }]);
23979               </script>
23980               <form ng-submit="submit()" ng-controller="ExampleController">
23981                 Enter text and hit enter:
23982                 <input type="text" ng-model="text" name="text" />
23983                 <input type="submit" id="submit" value="Submit" />
23984                 <pre>list={{list}}</pre>
23985               </form>
23986              </file>
23987              <file name="protractor.js" type="protractor">
23988                it('should check ng-submit', function() {
23989                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23990                  element(by.css('#submit')).click();
23991                  expect(element(by.binding('list')).getText()).toContain('hello');
23992                  expect(element(by.model('text')).getAttribute('value')).toBe('');
23993                });
23994                it('should ignore empty strings', function() {
23995                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23996                  element(by.css('#submit')).click();
23997                  element(by.css('#submit')).click();
23998                  expect(element(by.binding('list')).getText()).toContain('hello');
23999                 });
24000              </file>
24001            </example>
24002          */
24003
24004         /**
24005          * @ngdoc directive
24006          * @name ngFocus
24007          *
24008          * @description
24009          * Specify custom behavior on focus event.
24010          *
24011          * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24012          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24013          * during an `$apply` to ensure a consistent state.
24014          *
24015          * @element window, input, select, textarea, a
24016          * @priority 0
24017          * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24018          * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24019          *
24020          * @example
24021          * See {@link ng.directive:ngClick ngClick}
24022          */
24023
24024         /**
24025          * @ngdoc directive
24026          * @name ngBlur
24027          *
24028          * @description
24029          * Specify custom behavior on blur event.
24030          *
24031          * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24032          * an element has lost focus.
24033          *
24034          * Note: As the `blur` event is executed synchronously also during DOM manipulations
24035          * (e.g. removing a focussed input),
24036          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24037          * during an `$apply` to ensure a consistent state.
24038          *
24039          * @element window, input, select, textarea, a
24040          * @priority 0
24041          * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24042          * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24043          *
24044          * @example
24045          * See {@link ng.directive:ngClick ngClick}
24046          */
24047
24048         /**
24049          * @ngdoc directive
24050          * @name ngCopy
24051          *
24052          * @description
24053          * Specify custom behavior on copy event.
24054          *
24055          * @element window, input, select, textarea, a
24056          * @priority 0
24057          * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24058          * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24059          *
24060          * @example
24061            <example>
24062              <file name="index.html">
24063               <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24064               copied: {{copied}}
24065              </file>
24066            </example>
24067          */
24068
24069         /**
24070          * @ngdoc directive
24071          * @name ngCut
24072          *
24073          * @description
24074          * Specify custom behavior on cut event.
24075          *
24076          * @element window, input, select, textarea, a
24077          * @priority 0
24078          * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24079          * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24080          *
24081          * @example
24082            <example>
24083              <file name="index.html">
24084               <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24085               cut: {{cut}}
24086              </file>
24087            </example>
24088          */
24089
24090         /**
24091          * @ngdoc directive
24092          * @name ngPaste
24093          *
24094          * @description
24095          * Specify custom behavior on paste event.
24096          *
24097          * @element window, input, select, textarea, a
24098          * @priority 0
24099          * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24100          * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24101          *
24102          * @example
24103            <example>
24104              <file name="index.html">
24105               <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24106               pasted: {{paste}}
24107              </file>
24108            </example>
24109          */
24110
24111         /**
24112          * @ngdoc directive
24113          * @name ngIf
24114          * @restrict A
24115          * @multiElement
24116          *
24117          * @description
24118          * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24119          * {expression}. If the expression assigned to `ngIf` evaluates to a false
24120          * value then the element is removed from the DOM, otherwise a clone of the
24121          * element is reinserted into the DOM.
24122          *
24123          * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24124          * element in the DOM rather than changing its visibility via the `display` css property.  A common
24125          * case when this difference is significant is when using css selectors that rely on an element's
24126          * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24127          *
24128          * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24129          * is created when the element is restored.  The scope created within `ngIf` inherits from
24130          * its parent scope using
24131          * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24132          * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24133          * a javascript primitive defined in the parent scope. In this case any modifications made to the
24134          * variable within the child scope will override (hide) the value in the parent scope.
24135          *
24136          * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24137          * is if an element's class attribute is directly modified after it's compiled, using something like
24138          * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24139          * the added class will be lost because the original compiled state is used to regenerate the element.
24140          *
24141          * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24142          * and `leave` effects.
24143          *
24144          * @animations
24145          * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24146          * leave - happens just before the `ngIf` contents are removed from the DOM
24147          *
24148          * @element ANY
24149          * @scope
24150          * @priority 600
24151          * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24152          *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24153          *     element is added to the DOM tree.
24154          *
24155          * @example
24156           <example module="ngAnimate" deps="angular-animate.js" animations="true">
24157             <file name="index.html">
24158               <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24159               Show when checked:
24160               <span ng-if="checked" class="animate-if">
24161                 This is removed when the checkbox is unchecked.
24162               </span>
24163             </file>
24164             <file name="animations.css">
24165               .animate-if {
24166                 background:white;
24167                 border:1px solid black;
24168                 padding:10px;
24169               }
24170
24171               .animate-if.ng-enter, .animate-if.ng-leave {
24172                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24173               }
24174
24175               .animate-if.ng-enter,
24176               .animate-if.ng-leave.ng-leave-active {
24177                 opacity:0;
24178               }
24179
24180               .animate-if.ng-leave,
24181               .animate-if.ng-enter.ng-enter-active {
24182                 opacity:1;
24183               }
24184             </file>
24185           </example>
24186          */
24187         var ngIfDirective = ['$animate', function($animate) {
24188           return {
24189             multiElement: true,
24190             transclude: 'element',
24191             priority: 600,
24192             terminal: true,
24193             restrict: 'A',
24194             $$tlb: true,
24195             link: function($scope, $element, $attr, ctrl, $transclude) {
24196                 var block, childScope, previousElements;
24197                 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24198
24199                   if (value) {
24200                     if (!childScope) {
24201                       $transclude(function(clone, newScope) {
24202                         childScope = newScope;
24203                         clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24204                         // Note: We only need the first/last node of the cloned nodes.
24205                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24206                         // by a directive with templateUrl when its template arrives.
24207                         block = {
24208                           clone: clone
24209                         };
24210                         $animate.enter(clone, $element.parent(), $element);
24211                       });
24212                     }
24213                   } else {
24214                     if (previousElements) {
24215                       previousElements.remove();
24216                       previousElements = null;
24217                     }
24218                     if (childScope) {
24219                       childScope.$destroy();
24220                       childScope = null;
24221                     }
24222                     if (block) {
24223                       previousElements = getBlockNodes(block.clone);
24224                       $animate.leave(previousElements).then(function() {
24225                         previousElements = null;
24226                       });
24227                       block = null;
24228                     }
24229                   }
24230                 });
24231             }
24232           };
24233         }];
24234
24235         /**
24236          * @ngdoc directive
24237          * @name ngInclude
24238          * @restrict ECA
24239          *
24240          * @description
24241          * Fetches, compiles and includes an external HTML fragment.
24242          *
24243          * By default, the template URL is restricted to the same domain and protocol as the
24244          * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24245          * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24246          * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24247          * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24248          * ng.$sce Strict Contextual Escaping}.
24249          *
24250          * In addition, the browser's
24251          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24252          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24253          * policy may further restrict whether the template is successfully loaded.
24254          * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24255          * access on some browsers.
24256          *
24257          * @animations
24258          * enter - animation is used to bring new content into the browser.
24259          * leave - animation is used to animate existing content away.
24260          *
24261          * The enter and leave animation occur concurrently.
24262          *
24263          * @scope
24264          * @priority 400
24265          *
24266          * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24267          *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24268          * @param {string=} onload Expression to evaluate when a new partial is loaded.
24269          *                  <div class="alert alert-warning">
24270          *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24271          *                  a function with the name on the window element, which will usually throw a
24272          *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24273          *                  different form that {@link guide/directive#normalization matches} `onload`.
24274          *                  </div>
24275            *
24276          * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24277          *                  $anchorScroll} to scroll the viewport after the content is loaded.
24278          *
24279          *                  - If the attribute is not set, disable scrolling.
24280          *                  - If the attribute is set without value, enable scrolling.
24281          *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24282          *
24283          * @example
24284           <example module="includeExample" deps="angular-animate.js" animations="true">
24285             <file name="index.html">
24286              <div ng-controller="ExampleController">
24287                <select ng-model="template" ng-options="t.name for t in templates">
24288                 <option value="">(blank)</option>
24289                </select>
24290                url of the template: <code>{{template.url}}</code>
24291                <hr/>
24292                <div class="slide-animate-container">
24293                  <div class="slide-animate" ng-include="template.url"></div>
24294                </div>
24295              </div>
24296             </file>
24297             <file name="script.js">
24298               angular.module('includeExample', ['ngAnimate'])
24299                 .controller('ExampleController', ['$scope', function($scope) {
24300                   $scope.templates =
24301                     [ { name: 'template1.html', url: 'template1.html'},
24302                       { name: 'template2.html', url: 'template2.html'} ];
24303                   $scope.template = $scope.templates[0];
24304                 }]);
24305              </file>
24306             <file name="template1.html">
24307               Content of template1.html
24308             </file>
24309             <file name="template2.html">
24310               Content of template2.html
24311             </file>
24312             <file name="animations.css">
24313               .slide-animate-container {
24314                 position:relative;
24315                 background:white;
24316                 border:1px solid black;
24317                 height:40px;
24318                 overflow:hidden;
24319               }
24320
24321               .slide-animate {
24322                 padding:10px;
24323               }
24324
24325               .slide-animate.ng-enter, .slide-animate.ng-leave {
24326                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24327
24328                 position:absolute;
24329                 top:0;
24330                 left:0;
24331                 right:0;
24332                 bottom:0;
24333                 display:block;
24334                 padding:10px;
24335               }
24336
24337               .slide-animate.ng-enter {
24338                 top:-50px;
24339               }
24340               .slide-animate.ng-enter.ng-enter-active {
24341                 top:0;
24342               }
24343
24344               .slide-animate.ng-leave {
24345                 top:0;
24346               }
24347               .slide-animate.ng-leave.ng-leave-active {
24348                 top:50px;
24349               }
24350             </file>
24351             <file name="protractor.js" type="protractor">
24352               var templateSelect = element(by.model('template'));
24353               var includeElem = element(by.css('[ng-include]'));
24354
24355               it('should load template1.html', function() {
24356                 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24357               });
24358
24359               it('should load template2.html', function() {
24360                 if (browser.params.browser == 'firefox') {
24361                   // Firefox can't handle using selects
24362                   // See https://github.com/angular/protractor/issues/480
24363                   return;
24364                 }
24365                 templateSelect.click();
24366                 templateSelect.all(by.css('option')).get(2).click();
24367                 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24368               });
24369
24370               it('should change to blank', function() {
24371                 if (browser.params.browser == 'firefox') {
24372                   // Firefox can't handle using selects
24373                   return;
24374                 }
24375                 templateSelect.click();
24376                 templateSelect.all(by.css('option')).get(0).click();
24377                 expect(includeElem.isPresent()).toBe(false);
24378               });
24379             </file>
24380           </example>
24381          */
24382
24383
24384         /**
24385          * @ngdoc event
24386          * @name ngInclude#$includeContentRequested
24387          * @eventType emit on the scope ngInclude was declared in
24388          * @description
24389          * Emitted every time the ngInclude content is requested.
24390          *
24391          * @param {Object} angularEvent Synthetic event object.
24392          * @param {String} src URL of content to load.
24393          */
24394
24395
24396         /**
24397          * @ngdoc event
24398          * @name ngInclude#$includeContentLoaded
24399          * @eventType emit on the current ngInclude scope
24400          * @description
24401          * Emitted every time the ngInclude content is reloaded.
24402          *
24403          * @param {Object} angularEvent Synthetic event object.
24404          * @param {String} src URL of content to load.
24405          */
24406
24407
24408         /**
24409          * @ngdoc event
24410          * @name ngInclude#$includeContentError
24411          * @eventType emit on the scope ngInclude was declared in
24412          * @description
24413          * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24414          *
24415          * @param {Object} angularEvent Synthetic event object.
24416          * @param {String} src URL of content to load.
24417          */
24418         var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24419                           function($templateRequest,   $anchorScroll,   $animate) {
24420           return {
24421             restrict: 'ECA',
24422             priority: 400,
24423             terminal: true,
24424             transclude: 'element',
24425             controller: angular.noop,
24426             compile: function(element, attr) {
24427               var srcExp = attr.ngInclude || attr.src,
24428                   onloadExp = attr.onload || '',
24429                   autoScrollExp = attr.autoscroll;
24430
24431               return function(scope, $element, $attr, ctrl, $transclude) {
24432                 var changeCounter = 0,
24433                     currentScope,
24434                     previousElement,
24435                     currentElement;
24436
24437                 var cleanupLastIncludeContent = function() {
24438                   if (previousElement) {
24439                     previousElement.remove();
24440                     previousElement = null;
24441                   }
24442                   if (currentScope) {
24443                     currentScope.$destroy();
24444                     currentScope = null;
24445                   }
24446                   if (currentElement) {
24447                     $animate.leave(currentElement).then(function() {
24448                       previousElement = null;
24449                     });
24450                     previousElement = currentElement;
24451                     currentElement = null;
24452                   }
24453                 };
24454
24455                 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24456                   var afterAnimation = function() {
24457                     if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24458                       $anchorScroll();
24459                     }
24460                   };
24461                   var thisChangeId = ++changeCounter;
24462
24463                   if (src) {
24464                     //set the 2nd param to true to ignore the template request error so that the inner
24465                     //contents and scope can be cleaned up.
24466                     $templateRequest(src, true).then(function(response) {
24467                       if (thisChangeId !== changeCounter) return;
24468                       var newScope = scope.$new();
24469                       ctrl.template = response;
24470
24471                       // Note: This will also link all children of ng-include that were contained in the original
24472                       // html. If that content contains controllers, ... they could pollute/change the scope.
24473                       // However, using ng-include on an element with additional content does not make sense...
24474                       // Note: We can't remove them in the cloneAttchFn of $transclude as that
24475                       // function is called before linking the content, which would apply child
24476                       // directives to non existing elements.
24477                       var clone = $transclude(newScope, function(clone) {
24478                         cleanupLastIncludeContent();
24479                         $animate.enter(clone, null, $element).then(afterAnimation);
24480                       });
24481
24482                       currentScope = newScope;
24483                       currentElement = clone;
24484
24485                       currentScope.$emit('$includeContentLoaded', src);
24486                       scope.$eval(onloadExp);
24487                     }, function() {
24488                       if (thisChangeId === changeCounter) {
24489                         cleanupLastIncludeContent();
24490                         scope.$emit('$includeContentError', src);
24491                       }
24492                     });
24493                     scope.$emit('$includeContentRequested', src);
24494                   } else {
24495                     cleanupLastIncludeContent();
24496                     ctrl.template = null;
24497                   }
24498                 });
24499               };
24500             }
24501           };
24502         }];
24503
24504         // This directive is called during the $transclude call of the first `ngInclude` directive.
24505         // It will replace and compile the content of the element with the loaded template.
24506         // We need this directive so that the element content is already filled when
24507         // the link function of another directive on the same element as ngInclude
24508         // is called.
24509         var ngIncludeFillContentDirective = ['$compile',
24510           function($compile) {
24511             return {
24512               restrict: 'ECA',
24513               priority: -400,
24514               require: 'ngInclude',
24515               link: function(scope, $element, $attr, ctrl) {
24516                 if (/SVG/.test($element[0].toString())) {
24517                   // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24518                   // support innerHTML, so detect this here and try to generate the contents
24519                   // specially.
24520                   $element.empty();
24521                   $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24522                       function namespaceAdaptedClone(clone) {
24523                     $element.append(clone);
24524                   }, {futureParentElement: $element});
24525                   return;
24526                 }
24527
24528                 $element.html(ctrl.template);
24529                 $compile($element.contents())(scope);
24530               }
24531             };
24532           }];
24533
24534         /**
24535          * @ngdoc directive
24536          * @name ngInit
24537          * @restrict AC
24538          *
24539          * @description
24540          * The `ngInit` directive allows you to evaluate an expression in the
24541          * current scope.
24542          *
24543          * <div class="alert alert-danger">
24544          * This directive can be abused to add unnecessary amounts of logic into your templates.
24545          * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24546          * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24547          * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24548          * rather than `ngInit` to initialize values on a scope.
24549          * </div>
24550          *
24551          * <div class="alert alert-warning">
24552          * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24553          * sure you have parentheses to ensure correct operator precedence:
24554          * <pre class="prettyprint">
24555          * `<div ng-init="test1 = ($index | toString)"></div>`
24556          * </pre>
24557          * </div>
24558          *
24559          * @priority 450
24560          *
24561          * @element ANY
24562          * @param {expression} ngInit {@link guide/expression Expression} to eval.
24563          *
24564          * @example
24565            <example module="initExample">
24566              <file name="index.html">
24567            <script>
24568              angular.module('initExample', [])
24569                .controller('ExampleController', ['$scope', function($scope) {
24570                  $scope.list = [['a', 'b'], ['c', 'd']];
24571                }]);
24572            </script>
24573            <div ng-controller="ExampleController">
24574              <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24575                <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24576                   <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24577                </div>
24578              </div>
24579            </div>
24580              </file>
24581              <file name="protractor.js" type="protractor">
24582                it('should alias index positions', function() {
24583                  var elements = element.all(by.css('.example-init'));
24584                  expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24585                  expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24586                  expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24587                  expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24588                });
24589              </file>
24590            </example>
24591          */
24592         var ngInitDirective = ngDirective({
24593           priority: 450,
24594           compile: function() {
24595             return {
24596               pre: function(scope, element, attrs) {
24597                 scope.$eval(attrs.ngInit);
24598               }
24599             };
24600           }
24601         });
24602
24603         /**
24604          * @ngdoc directive
24605          * @name ngList
24606          *
24607          * @description
24608          * Text input that converts between a delimited string and an array of strings. The default
24609          * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24610          * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24611          *
24612          * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24613          * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24614          *   list item is respected. This implies that the user of the directive is responsible for
24615          *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24616          *   tab or newline character.
24617          * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24618          *   when joining the list items back together) and whitespace around each list item is stripped
24619          *   before it is added to the model.
24620          *
24621          * ### Example with Validation
24622          *
24623          * <example name="ngList-directive" module="listExample">
24624          *   <file name="app.js">
24625          *      angular.module('listExample', [])
24626          *        .controller('ExampleController', ['$scope', function($scope) {
24627          *          $scope.names = ['morpheus', 'neo', 'trinity'];
24628          *        }]);
24629          *   </file>
24630          *   <file name="index.html">
24631          *    <form name="myForm" ng-controller="ExampleController">
24632          *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24633          *      <span role="alert">
24634          *        <span class="error" ng-show="myForm.namesInput.$error.required">
24635          *        Required!</span>
24636          *      </span>
24637          *      <br>
24638          *      <tt>names = {{names}}</tt><br/>
24639          *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24640          *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24641          *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24642          *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24643          *     </form>
24644          *   </file>
24645          *   <file name="protractor.js" type="protractor">
24646          *     var listInput = element(by.model('names'));
24647          *     var names = element(by.exactBinding('names'));
24648          *     var valid = element(by.binding('myForm.namesInput.$valid'));
24649          *     var error = element(by.css('span.error'));
24650          *
24651          *     it('should initialize to model', function() {
24652          *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24653          *       expect(valid.getText()).toContain('true');
24654          *       expect(error.getCssValue('display')).toBe('none');
24655          *     });
24656          *
24657          *     it('should be invalid if empty', function() {
24658          *       listInput.clear();
24659          *       listInput.sendKeys('');
24660          *
24661          *       expect(names.getText()).toContain('');
24662          *       expect(valid.getText()).toContain('false');
24663          *       expect(error.getCssValue('display')).not.toBe('none');
24664          *     });
24665          *   </file>
24666          * </example>
24667          *
24668          * ### Example - splitting on newline
24669          * <example name="ngList-directive-newlines">
24670          *   <file name="index.html">
24671          *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24672          *    <pre>{{ list | json }}</pre>
24673          *   </file>
24674          *   <file name="protractor.js" type="protractor">
24675          *     it("should split the text by newlines", function() {
24676          *       var listInput = element(by.model('list'));
24677          *       var output = element(by.binding('list | json'));
24678          *       listInput.sendKeys('abc\ndef\nghi');
24679          *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24680          *     });
24681          *   </file>
24682          * </example>
24683          *
24684          * @element input
24685          * @param {string=} ngList optional delimiter that should be used to split the value.
24686          */
24687         var ngListDirective = function() {
24688           return {
24689             restrict: 'A',
24690             priority: 100,
24691             require: 'ngModel',
24692             link: function(scope, element, attr, ctrl) {
24693               // We want to control whitespace trimming so we use this convoluted approach
24694               // to access the ngList attribute, which doesn't pre-trim the attribute
24695               var ngList = element.attr(attr.$attr.ngList) || ', ';
24696               var trimValues = attr.ngTrim !== 'false';
24697               var separator = trimValues ? trim(ngList) : ngList;
24698
24699               var parse = function(viewValue) {
24700                 // If the viewValue is invalid (say required but empty) it will be `undefined`
24701                 if (isUndefined(viewValue)) return;
24702
24703                 var list = [];
24704
24705                 if (viewValue) {
24706                   forEach(viewValue.split(separator), function(value) {
24707                     if (value) list.push(trimValues ? trim(value) : value);
24708                   });
24709                 }
24710
24711                 return list;
24712               };
24713
24714               ctrl.$parsers.push(parse);
24715               ctrl.$formatters.push(function(value) {
24716                 if (isArray(value)) {
24717                   return value.join(ngList);
24718                 }
24719
24720                 return undefined;
24721               });
24722
24723               // Override the standard $isEmpty because an empty array means the input is empty.
24724               ctrl.$isEmpty = function(value) {
24725                 return !value || !value.length;
24726               };
24727             }
24728           };
24729         };
24730
24731         /* global VALID_CLASS: true,
24732           INVALID_CLASS: true,
24733           PRISTINE_CLASS: true,
24734           DIRTY_CLASS: true,
24735           UNTOUCHED_CLASS: true,
24736           TOUCHED_CLASS: true,
24737         */
24738
24739         var VALID_CLASS = 'ng-valid',
24740             INVALID_CLASS = 'ng-invalid',
24741             PRISTINE_CLASS = 'ng-pristine',
24742             DIRTY_CLASS = 'ng-dirty',
24743             UNTOUCHED_CLASS = 'ng-untouched',
24744             TOUCHED_CLASS = 'ng-touched',
24745             PENDING_CLASS = 'ng-pending';
24746
24747         var ngModelMinErr = minErr('ngModel');
24748
24749         /**
24750          * @ngdoc type
24751          * @name ngModel.NgModelController
24752          *
24753          * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24754          * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24755          * is set.
24756          * @property {*} $modelValue The value in the model that the control is bound to.
24757          * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24758                the control reads value from the DOM. The functions are called in array order, each passing
24759                its return value through to the next. The last return value is forwarded to the
24760                {@link ngModel.NgModelController#$validators `$validators`} collection.
24761
24762         Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24763         `$viewValue`}.
24764
24765         Returning `undefined` from a parser means a parse error occurred. In that case,
24766         no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24767         will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24768         is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24769
24770          *
24771          * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24772                the model value changes. The functions are called in reverse array order, each passing the value through to the
24773                next. The last return value is used as the actual DOM value.
24774                Used to format / convert values for display in the control.
24775          * ```js
24776          * function formatter(value) {
24777          *   if (value) {
24778          *     return value.toUpperCase();
24779          *   }
24780          * }
24781          * ngModel.$formatters.push(formatter);
24782          * ```
24783          *
24784          * @property {Object.<string, function>} $validators A collection of validators that are applied
24785          *      whenever the model value changes. The key value within the object refers to the name of the
24786          *      validator while the function refers to the validation operation. The validation operation is
24787          *      provided with the model value as an argument and must return a true or false value depending
24788          *      on the response of that validation.
24789          *
24790          * ```js
24791          * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24792          *   var value = modelValue || viewValue;
24793          *   return /[0-9]+/.test(value) &&
24794          *          /[a-z]+/.test(value) &&
24795          *          /[A-Z]+/.test(value) &&
24796          *          /\W+/.test(value);
24797          * };
24798          * ```
24799          *
24800          * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24801          *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24802          *      is expected to return a promise when it is run during the model validation process. Once the promise
24803          *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24804          *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24805          *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24806          *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24807          *      will only run once all synchronous validators have passed.
24808          *
24809          * Please note that if $http is used then it is important that the server returns a success HTTP response code
24810          * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24811          *
24812          * ```js
24813          * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24814          *   var value = modelValue || viewValue;
24815          *
24816          *   // Lookup user by username
24817          *   return $http.get('/api/users/' + value).
24818          *      then(function resolved() {
24819          *        //username exists, this means validation fails
24820          *        return $q.reject('exists');
24821          *      }, function rejected() {
24822          *        //username does not exist, therefore this validation passes
24823          *        return true;
24824          *      });
24825          * };
24826          * ```
24827          *
24828          * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24829          *     view value has changed. It is called with no arguments, and its return value is ignored.
24830          *     This can be used in place of additional $watches against the model value.
24831          *
24832          * @property {Object} $error An object hash with all failing validator ids as keys.
24833          * @property {Object} $pending An object hash with all pending validator ids as keys.
24834          *
24835          * @property {boolean} $untouched True if control has not lost focus yet.
24836          * @property {boolean} $touched True if control has lost focus.
24837          * @property {boolean} $pristine True if user has not interacted with the control yet.
24838          * @property {boolean} $dirty True if user has already interacted with the control.
24839          * @property {boolean} $valid True if there is no error.
24840          * @property {boolean} $invalid True if at least one error on the control.
24841          * @property {string} $name The name attribute of the control.
24842          *
24843          * @description
24844          *
24845          * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24846          * The controller contains services for data-binding, validation, CSS updates, and value formatting
24847          * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24848          * listening to DOM events.
24849          * Such DOM related logic should be provided by other directives which make use of
24850          * `NgModelController` for data-binding to control elements.
24851          * Angular provides this DOM logic for most {@link input `input`} elements.
24852          * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24853          * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24854          *
24855          * @example
24856          * ### Custom Control Example
24857          * This example shows how to use `NgModelController` with a custom control to achieve
24858          * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24859          * collaborate together to achieve the desired result.
24860          *
24861          * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24862          * contents be edited in place by the user.
24863          *
24864          * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24865          * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24866          * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24867          * that content using the `$sce` service.
24868          *
24869          * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24870             <file name="style.css">
24871               [contenteditable] {
24872                 border: 1px solid black;
24873                 background-color: white;
24874                 min-height: 20px;
24875               }
24876
24877               .ng-invalid {
24878                 border: 1px solid red;
24879               }
24880
24881             </file>
24882             <file name="script.js">
24883               angular.module('customControl', ['ngSanitize']).
24884                 directive('contenteditable', ['$sce', function($sce) {
24885                   return {
24886                     restrict: 'A', // only activate on element attribute
24887                     require: '?ngModel', // get a hold of NgModelController
24888                     link: function(scope, element, attrs, ngModel) {
24889                       if (!ngModel) return; // do nothing if no ng-model
24890
24891                       // Specify how UI should be updated
24892                       ngModel.$render = function() {
24893                         element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24894                       };
24895
24896                       // Listen for change events to enable binding
24897                       element.on('blur keyup change', function() {
24898                         scope.$evalAsync(read);
24899                       });
24900                       read(); // initialize
24901
24902                       // Write data to the model
24903                       function read() {
24904                         var html = element.html();
24905                         // When we clear the content editable the browser leaves a <br> behind
24906                         // If strip-br attribute is provided then we strip this out
24907                         if ( attrs.stripBr && html == '<br>' ) {
24908                           html = '';
24909                         }
24910                         ngModel.$setViewValue(html);
24911                       }
24912                     }
24913                   };
24914                 }]);
24915             </file>
24916             <file name="index.html">
24917               <form name="myForm">
24918                <div contenteditable
24919                     name="myWidget" ng-model="userContent"
24920                     strip-br="true"
24921                     required>Change me!</div>
24922                 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24923                <hr>
24924                <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24925               </form>
24926             </file>
24927             <file name="protractor.js" type="protractor">
24928             it('should data-bind and become invalid', function() {
24929               if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24930                 // SafariDriver can't handle contenteditable
24931                 // and Firefox driver can't clear contenteditables very well
24932                 return;
24933               }
24934               var contentEditable = element(by.css('[contenteditable]'));
24935               var content = 'Change me!';
24936
24937               expect(contentEditable.getText()).toEqual(content);
24938
24939               contentEditable.clear();
24940               contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24941               expect(contentEditable.getText()).toEqual('');
24942               expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24943             });
24944             </file>
24945          * </example>
24946          *
24947          *
24948          */
24949         var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24950             function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24951           this.$viewValue = Number.NaN;
24952           this.$modelValue = Number.NaN;
24953           this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24954           this.$validators = {};
24955           this.$asyncValidators = {};
24956           this.$parsers = [];
24957           this.$formatters = [];
24958           this.$viewChangeListeners = [];
24959           this.$untouched = true;
24960           this.$touched = false;
24961           this.$pristine = true;
24962           this.$dirty = false;
24963           this.$valid = true;
24964           this.$invalid = false;
24965           this.$error = {}; // keep invalid keys here
24966           this.$$success = {}; // keep valid keys here
24967           this.$pending = undefined; // keep pending keys here
24968           this.$name = $interpolate($attr.name || '', false)($scope);
24969           this.$$parentForm = nullFormCtrl;
24970
24971           var parsedNgModel = $parse($attr.ngModel),
24972               parsedNgModelAssign = parsedNgModel.assign,
24973               ngModelGet = parsedNgModel,
24974               ngModelSet = parsedNgModelAssign,
24975               pendingDebounce = null,
24976               parserValid,
24977               ctrl = this;
24978
24979           this.$$setOptions = function(options) {
24980             ctrl.$options = options;
24981             if (options && options.getterSetter) {
24982               var invokeModelGetter = $parse($attr.ngModel + '()'),
24983                   invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24984
24985               ngModelGet = function($scope) {
24986                 var modelValue = parsedNgModel($scope);
24987                 if (isFunction(modelValue)) {
24988                   modelValue = invokeModelGetter($scope);
24989                 }
24990                 return modelValue;
24991               };
24992               ngModelSet = function($scope, newValue) {
24993                 if (isFunction(parsedNgModel($scope))) {
24994                   invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24995                 } else {
24996                   parsedNgModelAssign($scope, ctrl.$modelValue);
24997                 }
24998               };
24999             } else if (!parsedNgModel.assign) {
25000               throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25001                   $attr.ngModel, startingTag($element));
25002             }
25003           };
25004
25005           /**
25006            * @ngdoc method
25007            * @name ngModel.NgModelController#$render
25008            *
25009            * @description
25010            * Called when the view needs to be updated. It is expected that the user of the ng-model
25011            * directive will implement this method.
25012            *
25013            * The `$render()` method is invoked in the following situations:
25014            *
25015            * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25016            *   committed value then `$render()` is called to update the input control.
25017            * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25018            *   the `$viewValue` are different from last time.
25019            *
25020            * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25021            * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25022            * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25023            * invoked if you only change a property on the objects.
25024            */
25025           this.$render = noop;
25026
25027           /**
25028            * @ngdoc method
25029            * @name ngModel.NgModelController#$isEmpty
25030            *
25031            * @description
25032            * This is called when we need to determine if the value of an input is empty.
25033            *
25034            * For instance, the required directive does this to work out if the input has data or not.
25035            *
25036            * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25037            *
25038            * You can override this for input directives whose concept of being empty is different from the
25039            * default. The `checkboxInputType` directive does this because in its case a value of `false`
25040            * implies empty.
25041            *
25042            * @param {*} value The value of the input to check for emptiness.
25043            * @returns {boolean} True if `value` is "empty".
25044            */
25045           this.$isEmpty = function(value) {
25046             return isUndefined(value) || value === '' || value === null || value !== value;
25047           };
25048
25049           var currentValidationRunId = 0;
25050
25051           /**
25052            * @ngdoc method
25053            * @name ngModel.NgModelController#$setValidity
25054            *
25055            * @description
25056            * Change the validity state, and notify the form.
25057            *
25058            * This method can be called within $parsers/$formatters or a custom validation implementation.
25059            * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25060            * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25061            *
25062            * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25063            *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25064            *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25065            *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25066            *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25067            *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25068            * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25069            *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25070            *                          Skipped is used by Angular when validators do not run because of parse errors and
25071            *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25072            */
25073           addSetValidityMethod({
25074             ctrl: this,
25075             $element: $element,
25076             set: function(object, property) {
25077               object[property] = true;
25078             },
25079             unset: function(object, property) {
25080               delete object[property];
25081             },
25082             $animate: $animate
25083           });
25084
25085           /**
25086            * @ngdoc method
25087            * @name ngModel.NgModelController#$setPristine
25088            *
25089            * @description
25090            * Sets the control to its pristine state.
25091            *
25092            * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25093            * state (`ng-pristine` class). A model is considered to be pristine when the control
25094            * has not been changed from when first compiled.
25095            */
25096           this.$setPristine = function() {
25097             ctrl.$dirty = false;
25098             ctrl.$pristine = true;
25099             $animate.removeClass($element, DIRTY_CLASS);
25100             $animate.addClass($element, PRISTINE_CLASS);
25101           };
25102
25103           /**
25104            * @ngdoc method
25105            * @name ngModel.NgModelController#$setDirty
25106            *
25107            * @description
25108            * Sets the control to its dirty state.
25109            *
25110            * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25111            * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25112            * from when first compiled.
25113            */
25114           this.$setDirty = function() {
25115             ctrl.$dirty = true;
25116             ctrl.$pristine = false;
25117             $animate.removeClass($element, PRISTINE_CLASS);
25118             $animate.addClass($element, DIRTY_CLASS);
25119             ctrl.$$parentForm.$setDirty();
25120           };
25121
25122           /**
25123            * @ngdoc method
25124            * @name ngModel.NgModelController#$setUntouched
25125            *
25126            * @description
25127            * Sets the control to its untouched state.
25128            *
25129            * This method can be called to remove the `ng-touched` class and set the control to its
25130            * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25131            * by default, however this function can be used to restore that state if the model has
25132            * already been touched by the user.
25133            */
25134           this.$setUntouched = function() {
25135             ctrl.$touched = false;
25136             ctrl.$untouched = true;
25137             $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25138           };
25139
25140           /**
25141            * @ngdoc method
25142            * @name ngModel.NgModelController#$setTouched
25143            *
25144            * @description
25145            * Sets the control to its touched state.
25146            *
25147            * This method can be called to remove the `ng-untouched` class and set the control to its
25148            * touched state (`ng-touched` class). A model is considered to be touched when the user has
25149            * first focused the control element and then shifted focus away from the control (blur event).
25150            */
25151           this.$setTouched = function() {
25152             ctrl.$touched = true;
25153             ctrl.$untouched = false;
25154             $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25155           };
25156
25157           /**
25158            * @ngdoc method
25159            * @name ngModel.NgModelController#$rollbackViewValue
25160            *
25161            * @description
25162            * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25163            * which may be caused by a pending debounced event or because the input is waiting for a some
25164            * future event.
25165            *
25166            * If you have an input that uses `ng-model-options` to set up debounced events or events such
25167            * as blur you can have a situation where there is a period when the `$viewValue`
25168            * is out of synch with the ngModel's `$modelValue`.
25169            *
25170            * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25171            * programmatically before these debounced/future events have resolved/occurred, because Angular's
25172            * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25173            *
25174            * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25175            * input which may have such events pending. This is important in order to make sure that the
25176            * input field will be updated with the new model value and any pending operations are cancelled.
25177            *
25178            * <example name="ng-model-cancel-update" module="cancel-update-example">
25179            *   <file name="app.js">
25180            *     angular.module('cancel-update-example', [])
25181            *
25182            *     .controller('CancelUpdateController', ['$scope', function($scope) {
25183            *       $scope.resetWithCancel = function(e) {
25184            *         if (e.keyCode == 27) {
25185            *           $scope.myForm.myInput1.$rollbackViewValue();
25186            *           $scope.myValue = '';
25187            *         }
25188            *       };
25189            *       $scope.resetWithoutCancel = function(e) {
25190            *         if (e.keyCode == 27) {
25191            *           $scope.myValue = '';
25192            *         }
25193            *       };
25194            *     }]);
25195            *   </file>
25196            *   <file name="index.html">
25197            *     <div ng-controller="CancelUpdateController">
25198            *       <p>Try typing something in each input.  See that the model only updates when you
25199            *          blur off the input.
25200            *        </p>
25201            *        <p>Now see what happens if you start typing then press the Escape key</p>
25202            *
25203            *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25204            *         <p id="inputDescription1">With $rollbackViewValue()</p>
25205            *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25206            *                ng-keydown="resetWithCancel($event)"><br/>
25207            *         myValue: "{{ myValue }}"
25208            *
25209            *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25210            *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25211            *                ng-keydown="resetWithoutCancel($event)"><br/>
25212            *         myValue: "{{ myValue }}"
25213            *       </form>
25214            *     </div>
25215            *   </file>
25216            * </example>
25217            */
25218           this.$rollbackViewValue = function() {
25219             $timeout.cancel(pendingDebounce);
25220             ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25221             ctrl.$render();
25222           };
25223
25224           /**
25225            * @ngdoc method
25226            * @name ngModel.NgModelController#$validate
25227            *
25228            * @description
25229            * Runs each of the registered validators (first synchronous validators and then
25230            * asynchronous validators).
25231            * If the validity changes to invalid, the model will be set to `undefined`,
25232            * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25233            * If the validity changes to valid, it will set the model to the last available valid
25234            * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25235            */
25236           this.$validate = function() {
25237             // ignore $validate before model is initialized
25238             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25239               return;
25240             }
25241
25242             var viewValue = ctrl.$$lastCommittedViewValue;
25243             // Note: we use the $$rawModelValue as $modelValue might have been
25244             // set to undefined during a view -> model update that found validation
25245             // errors. We can't parse the view here, since that could change
25246             // the model although neither viewValue nor the model on the scope changed
25247             var modelValue = ctrl.$$rawModelValue;
25248
25249             var prevValid = ctrl.$valid;
25250             var prevModelValue = ctrl.$modelValue;
25251
25252             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25253
25254             ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25255               // If there was no change in validity, don't update the model
25256               // This prevents changing an invalid modelValue to undefined
25257               if (!allowInvalid && prevValid !== allValid) {
25258                 // Note: Don't check ctrl.$valid here, as we could have
25259                 // external validators (e.g. calculated on the server),
25260                 // that just call $setValidity and need the model value
25261                 // to calculate their validity.
25262                 ctrl.$modelValue = allValid ? modelValue : undefined;
25263
25264                 if (ctrl.$modelValue !== prevModelValue) {
25265                   ctrl.$$writeModelToScope();
25266                 }
25267               }
25268             });
25269
25270           };
25271
25272           this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25273             currentValidationRunId++;
25274             var localValidationRunId = currentValidationRunId;
25275
25276             // check parser error
25277             if (!processParseErrors()) {
25278               validationDone(false);
25279               return;
25280             }
25281             if (!processSyncValidators()) {
25282               validationDone(false);
25283               return;
25284             }
25285             processAsyncValidators();
25286
25287             function processParseErrors() {
25288               var errorKey = ctrl.$$parserName || 'parse';
25289               if (isUndefined(parserValid)) {
25290                 setValidity(errorKey, null);
25291               } else {
25292                 if (!parserValid) {
25293                   forEach(ctrl.$validators, function(v, name) {
25294                     setValidity(name, null);
25295                   });
25296                   forEach(ctrl.$asyncValidators, function(v, name) {
25297                     setValidity(name, null);
25298                   });
25299                 }
25300                 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25301                 setValidity(errorKey, parserValid);
25302                 return parserValid;
25303               }
25304               return true;
25305             }
25306
25307             function processSyncValidators() {
25308               var syncValidatorsValid = true;
25309               forEach(ctrl.$validators, function(validator, name) {
25310                 var result = validator(modelValue, viewValue);
25311                 syncValidatorsValid = syncValidatorsValid && result;
25312                 setValidity(name, result);
25313               });
25314               if (!syncValidatorsValid) {
25315                 forEach(ctrl.$asyncValidators, function(v, name) {
25316                   setValidity(name, null);
25317                 });
25318                 return false;
25319               }
25320               return true;
25321             }
25322
25323             function processAsyncValidators() {
25324               var validatorPromises = [];
25325               var allValid = true;
25326               forEach(ctrl.$asyncValidators, function(validator, name) {
25327                 var promise = validator(modelValue, viewValue);
25328                 if (!isPromiseLike(promise)) {
25329                   throw ngModelMinErr("$asyncValidators",
25330                     "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25331                 }
25332                 setValidity(name, undefined);
25333                 validatorPromises.push(promise.then(function() {
25334                   setValidity(name, true);
25335                 }, function(error) {
25336                   allValid = false;
25337                   setValidity(name, false);
25338                 }));
25339               });
25340               if (!validatorPromises.length) {
25341                 validationDone(true);
25342               } else {
25343                 $q.all(validatorPromises).then(function() {
25344                   validationDone(allValid);
25345                 }, noop);
25346               }
25347             }
25348
25349             function setValidity(name, isValid) {
25350               if (localValidationRunId === currentValidationRunId) {
25351                 ctrl.$setValidity(name, isValid);
25352               }
25353             }
25354
25355             function validationDone(allValid) {
25356               if (localValidationRunId === currentValidationRunId) {
25357
25358                 doneCallback(allValid);
25359               }
25360             }
25361           };
25362
25363           /**
25364            * @ngdoc method
25365            * @name ngModel.NgModelController#$commitViewValue
25366            *
25367            * @description
25368            * Commit a pending update to the `$modelValue`.
25369            *
25370            * Updates may be pending by a debounced event or because the input is waiting for a some future
25371            * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25372            * usually handles calling this in response to input events.
25373            */
25374           this.$commitViewValue = function() {
25375             var viewValue = ctrl.$viewValue;
25376
25377             $timeout.cancel(pendingDebounce);
25378
25379             // If the view value has not changed then we should just exit, except in the case where there is
25380             // a native validator on the element. In this case the validation state may have changed even though
25381             // the viewValue has stayed empty.
25382             if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25383               return;
25384             }
25385             ctrl.$$lastCommittedViewValue = viewValue;
25386
25387             // change to dirty
25388             if (ctrl.$pristine) {
25389               this.$setDirty();
25390             }
25391             this.$$parseAndValidate();
25392           };
25393
25394           this.$$parseAndValidate = function() {
25395             var viewValue = ctrl.$$lastCommittedViewValue;
25396             var modelValue = viewValue;
25397             parserValid = isUndefined(modelValue) ? undefined : true;
25398
25399             if (parserValid) {
25400               for (var i = 0; i < ctrl.$parsers.length; i++) {
25401                 modelValue = ctrl.$parsers[i](modelValue);
25402                 if (isUndefined(modelValue)) {
25403                   parserValid = false;
25404                   break;
25405                 }
25406               }
25407             }
25408             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25409               // ctrl.$modelValue has not been touched yet...
25410               ctrl.$modelValue = ngModelGet($scope);
25411             }
25412             var prevModelValue = ctrl.$modelValue;
25413             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25414             ctrl.$$rawModelValue = modelValue;
25415
25416             if (allowInvalid) {
25417               ctrl.$modelValue = modelValue;
25418               writeToModelIfNeeded();
25419             }
25420
25421             // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25422             // This can happen if e.g. $setViewValue is called from inside a parser
25423             ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25424               if (!allowInvalid) {
25425                 // Note: Don't check ctrl.$valid here, as we could have
25426                 // external validators (e.g. calculated on the server),
25427                 // that just call $setValidity and need the model value
25428                 // to calculate their validity.
25429                 ctrl.$modelValue = allValid ? modelValue : undefined;
25430                 writeToModelIfNeeded();
25431               }
25432             });
25433
25434             function writeToModelIfNeeded() {
25435               if (ctrl.$modelValue !== prevModelValue) {
25436                 ctrl.$$writeModelToScope();
25437               }
25438             }
25439           };
25440
25441           this.$$writeModelToScope = function() {
25442             ngModelSet($scope, ctrl.$modelValue);
25443             forEach(ctrl.$viewChangeListeners, function(listener) {
25444               try {
25445                 listener();
25446               } catch (e) {
25447                 $exceptionHandler(e);
25448               }
25449             });
25450           };
25451
25452           /**
25453            * @ngdoc method
25454            * @name ngModel.NgModelController#$setViewValue
25455            *
25456            * @description
25457            * Update the view value.
25458            *
25459            * This method should be called when a control wants to change the view value; typically,
25460            * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25461            * directive calls it when the value of the input changes and {@link ng.directive:select select}
25462            * calls it when an option is selected.
25463            *
25464            * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25465            * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25466            * value sent directly for processing, finally to be applied to `$modelValue` and then the
25467            * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25468            * in the `$viewChangeListeners` list, are called.
25469            *
25470            * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25471            * and the `default` trigger is not listed, all those actions will remain pending until one of the
25472            * `updateOn` events is triggered on the DOM element.
25473            * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25474            * directive is used with a custom debounce for this particular event.
25475            * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25476            * is specified, once the timer runs out.
25477            *
25478            * When used with standard inputs, the view value will always be a string (which is in some cases
25479            * parsed into another type, such as a `Date` object for `input[date]`.)
25480            * However, custom controls might also pass objects to this method. In this case, we should make
25481            * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25482            * perform a deep watch of objects, it only looks for a change of identity. If you only change
25483            * the property of the object then ngModel will not realise that the object has changed and
25484            * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25485            * not change properties of the copy once it has been passed to `$setViewValue`.
25486            * Otherwise you may cause the model value on the scope to change incorrectly.
25487            *
25488            * <div class="alert alert-info">
25489            * In any case, the value passed to the method should always reflect the current value
25490            * of the control. For example, if you are calling `$setViewValue` for an input element,
25491            * you should pass the input DOM value. Otherwise, the control and the scope model become
25492            * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25493            * the control's DOM value in any way. If we want to change the control's DOM value
25494            * programmatically, we should update the `ngModel` scope expression. Its new value will be
25495            * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25496            * to update the DOM, and finally call `$validate` on it.
25497            * </div>
25498            *
25499            * @param {*} value value from the view.
25500            * @param {string} trigger Event that triggered the update.
25501            */
25502           this.$setViewValue = function(value, trigger) {
25503             ctrl.$viewValue = value;
25504             if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25505               ctrl.$$debounceViewValueCommit(trigger);
25506             }
25507           };
25508
25509           this.$$debounceViewValueCommit = function(trigger) {
25510             var debounceDelay = 0,
25511                 options = ctrl.$options,
25512                 debounce;
25513
25514             if (options && isDefined(options.debounce)) {
25515               debounce = options.debounce;
25516               if (isNumber(debounce)) {
25517                 debounceDelay = debounce;
25518               } else if (isNumber(debounce[trigger])) {
25519                 debounceDelay = debounce[trigger];
25520               } else if (isNumber(debounce['default'])) {
25521                 debounceDelay = debounce['default'];
25522               }
25523             }
25524
25525             $timeout.cancel(pendingDebounce);
25526             if (debounceDelay) {
25527               pendingDebounce = $timeout(function() {
25528                 ctrl.$commitViewValue();
25529               }, debounceDelay);
25530             } else if ($rootScope.$$phase) {
25531               ctrl.$commitViewValue();
25532             } else {
25533               $scope.$apply(function() {
25534                 ctrl.$commitViewValue();
25535               });
25536             }
25537           };
25538
25539           // model -> value
25540           // Note: we cannot use a normal scope.$watch as we want to detect the following:
25541           // 1. scope value is 'a'
25542           // 2. user enters 'b'
25543           // 3. ng-change kicks in and reverts scope value to 'a'
25544           //    -> scope value did not change since the last digest as
25545           //       ng-change executes in apply phase
25546           // 4. view should be changed back to 'a'
25547           $scope.$watch(function ngModelWatch() {
25548             var modelValue = ngModelGet($scope);
25549
25550             // if scope model value and ngModel value are out of sync
25551             // TODO(perf): why not move this to the action fn?
25552             if (modelValue !== ctrl.$modelValue &&
25553                // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25554                (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25555             ) {
25556               ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25557               parserValid = undefined;
25558
25559               var formatters = ctrl.$formatters,
25560                   idx = formatters.length;
25561
25562               var viewValue = modelValue;
25563               while (idx--) {
25564                 viewValue = formatters[idx](viewValue);
25565               }
25566               if (ctrl.$viewValue !== viewValue) {
25567                 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25568                 ctrl.$render();
25569
25570                 ctrl.$$runValidators(modelValue, viewValue, noop);
25571               }
25572             }
25573
25574             return modelValue;
25575           });
25576         }];
25577
25578
25579         /**
25580          * @ngdoc directive
25581          * @name ngModel
25582          *
25583          * @element input
25584          * @priority 1
25585          *
25586          * @description
25587          * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25588          * property on the scope using {@link ngModel.NgModelController NgModelController},
25589          * which is created and exposed by this directive.
25590          *
25591          * `ngModel` is responsible for:
25592          *
25593          * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25594          *   require.
25595          * - Providing validation behavior (i.e. required, number, email, url).
25596          * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25597          * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25598          * - Registering the control with its parent {@link ng.directive:form form}.
25599          *
25600          * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25601          * current scope. If the property doesn't already exist on this scope, it will be created
25602          * implicitly and added to the scope.
25603          *
25604          * For best practices on using `ngModel`, see:
25605          *
25606          *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25607          *
25608          * For basic examples, how to use `ngModel`, see:
25609          *
25610          *  - {@link ng.directive:input input}
25611          *    - {@link input[text] text}
25612          *    - {@link input[checkbox] checkbox}
25613          *    - {@link input[radio] radio}
25614          *    - {@link input[number] number}
25615          *    - {@link input[email] email}
25616          *    - {@link input[url] url}
25617          *    - {@link input[date] date}
25618          *    - {@link input[datetime-local] datetime-local}
25619          *    - {@link input[time] time}
25620          *    - {@link input[month] month}
25621          *    - {@link input[week] week}
25622          *  - {@link ng.directive:select select}
25623          *  - {@link ng.directive:textarea textarea}
25624          *
25625          * # CSS classes
25626          * The following CSS classes are added and removed on the associated input/select/textarea element
25627          * depending on the validity of the model.
25628          *
25629          *  - `ng-valid`: the model is valid
25630          *  - `ng-invalid`: the model is invalid
25631          *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25632          *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25633          *  - `ng-pristine`: the control hasn't been interacted with yet
25634          *  - `ng-dirty`: the control has been interacted with
25635          *  - `ng-touched`: the control has been blurred
25636          *  - `ng-untouched`: the control hasn't been blurred
25637          *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25638          *
25639          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25640          *
25641          * ## Animation Hooks
25642          *
25643          * Animations within models are triggered when any of the associated CSS classes are added and removed
25644          * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25645          * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25646          * The animations that are triggered within ngModel are similar to how they work in ngClass and
25647          * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25648          *
25649          * The following example shows a simple way to utilize CSS transitions to style an input element
25650          * that has been rendered as invalid after it has been validated:
25651          *
25652          * <pre>
25653          * //be sure to include ngAnimate as a module to hook into more
25654          * //advanced animations
25655          * .my-input {
25656          *   transition:0.5s linear all;
25657          *   background: white;
25658          * }
25659          * .my-input.ng-invalid {
25660          *   background: red;
25661          *   color:white;
25662          * }
25663          * </pre>
25664          *
25665          * @example
25666          * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25667              <file name="index.html">
25668                <script>
25669                 angular.module('inputExample', [])
25670                   .controller('ExampleController', ['$scope', function($scope) {
25671                     $scope.val = '1';
25672                   }]);
25673                </script>
25674                <style>
25675                  .my-input {
25676                    transition:all linear 0.5s;
25677                    background: transparent;
25678                  }
25679                  .my-input.ng-invalid {
25680                    color:white;
25681                    background: red;
25682                  }
25683                </style>
25684                <p id="inputDescription">
25685                 Update input to see transitions when valid/invalid.
25686                 Integer is a valid value.
25687                </p>
25688                <form name="testForm" ng-controller="ExampleController">
25689                  <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25690                         aria-describedby="inputDescription" />
25691                </form>
25692              </file>
25693          * </example>
25694          *
25695          * ## Binding to a getter/setter
25696          *
25697          * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25698          * function that returns a representation of the model when called with zero arguments, and sets
25699          * the internal state of a model when called with an argument. It's sometimes useful to use this
25700          * for models that have an internal representation that's different from what the model exposes
25701          * to the view.
25702          *
25703          * <div class="alert alert-success">
25704          * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25705          * frequently than other parts of your code.
25706          * </div>
25707          *
25708          * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25709          * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25710          * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25711          * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25712          *
25713          * The following example shows how to use `ngModel` with a getter/setter:
25714          *
25715          * @example
25716          * <example name="ngModel-getter-setter" module="getterSetterExample">
25717              <file name="index.html">
25718                <div ng-controller="ExampleController">
25719                  <form name="userForm">
25720                    <label>Name:
25721                      <input type="text" name="userName"
25722                             ng-model="user.name"
25723                             ng-model-options="{ getterSetter: true }" />
25724                    </label>
25725                  </form>
25726                  <pre>user.name = <span ng-bind="user.name()"></span></pre>
25727                </div>
25728              </file>
25729              <file name="app.js">
25730                angular.module('getterSetterExample', [])
25731                  .controller('ExampleController', ['$scope', function($scope) {
25732                    var _name = 'Brian';
25733                    $scope.user = {
25734                      name: function(newName) {
25735                       // Note that newName can be undefined for two reasons:
25736                       // 1. Because it is called as a getter and thus called with no arguments
25737                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25738                       //    input is invalid
25739                       return arguments.length ? (_name = newName) : _name;
25740                      }
25741                    };
25742                  }]);
25743              </file>
25744          * </example>
25745          */
25746         var ngModelDirective = ['$rootScope', function($rootScope) {
25747           return {
25748             restrict: 'A',
25749             require: ['ngModel', '^?form', '^?ngModelOptions'],
25750             controller: NgModelController,
25751             // Prelink needs to run before any input directive
25752             // so that we can set the NgModelOptions in NgModelController
25753             // before anyone else uses it.
25754             priority: 1,
25755             compile: function ngModelCompile(element) {
25756               // Setup initial state of the control
25757               element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25758
25759               return {
25760                 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25761                   var modelCtrl = ctrls[0],
25762                       formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25763
25764                   modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25765
25766                   // notify others, especially parent forms
25767                   formCtrl.$addControl(modelCtrl);
25768
25769                   attr.$observe('name', function(newValue) {
25770                     if (modelCtrl.$name !== newValue) {
25771                       modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25772                     }
25773                   });
25774
25775                   scope.$on('$destroy', function() {
25776                     modelCtrl.$$parentForm.$removeControl(modelCtrl);
25777                   });
25778                 },
25779                 post: function ngModelPostLink(scope, element, attr, ctrls) {
25780                   var modelCtrl = ctrls[0];
25781                   if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25782                     element.on(modelCtrl.$options.updateOn, function(ev) {
25783                       modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25784                     });
25785                   }
25786
25787                   element.on('blur', function(ev) {
25788                     if (modelCtrl.$touched) return;
25789
25790                     if ($rootScope.$$phase) {
25791                       scope.$evalAsync(modelCtrl.$setTouched);
25792                     } else {
25793                       scope.$apply(modelCtrl.$setTouched);
25794                     }
25795                   });
25796                 }
25797               };
25798             }
25799           };
25800         }];
25801
25802         var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25803
25804         /**
25805          * @ngdoc directive
25806          * @name ngModelOptions
25807          *
25808          * @description
25809          * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25810          * events that will trigger a model update and/or a debouncing delay so that the actual update only
25811          * takes place when a timer expires; this timer will be reset after another change takes place.
25812          *
25813          * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25814          * be different from the value in the actual model. This means that if you update the model you
25815          * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25816          * order to make sure it is synchronized with the model and that any debounced action is canceled.
25817          *
25818          * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25819          * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25820          * important because `form` controllers are published to the related scope under the name in their
25821          * `name` attribute.
25822          *
25823          * Any pending changes will take place immediately when an enclosing form is submitted via the
25824          * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25825          * to have access to the updated model.
25826          *
25827          * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25828          *
25829          * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25830          *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25831          *     events using an space delimited list. There is a special event called `default` that
25832          *     matches the default events belonging of the control.
25833          *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25834          *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25835          *     custom value for each event. For example:
25836          *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25837          *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25838          *     not validate correctly instead of the default behavior of setting the model to undefined.
25839          *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25840                `ngModel` as getters/setters.
25841          *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25842          *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25843          *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25844          *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25845          *     If not specified, the timezone of the browser will be used.
25846          *
25847          * @example
25848
25849           The following example shows how to override immediate updates. Changes on the inputs within the
25850           form will update the model only when the control loses focus (blur event). If `escape` key is
25851           pressed while the input field is focused, the value is reset to the value in the current model.
25852
25853           <example name="ngModelOptions-directive-blur" module="optionsExample">
25854             <file name="index.html">
25855               <div ng-controller="ExampleController">
25856                 <form name="userForm">
25857                   <label>Name:
25858                     <input type="text" name="userName"
25859                            ng-model="user.name"
25860                            ng-model-options="{ updateOn: 'blur' }"
25861                            ng-keyup="cancel($event)" />
25862                   </label><br />
25863                   <label>Other data:
25864                     <input type="text" ng-model="user.data" />
25865                   </label><br />
25866                 </form>
25867                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25868                 <pre>user.data = <span ng-bind="user.data"></span></pre>
25869               </div>
25870             </file>
25871             <file name="app.js">
25872               angular.module('optionsExample', [])
25873                 .controller('ExampleController', ['$scope', function($scope) {
25874                   $scope.user = { name: 'John', data: '' };
25875
25876                   $scope.cancel = function(e) {
25877                     if (e.keyCode == 27) {
25878                       $scope.userForm.userName.$rollbackViewValue();
25879                     }
25880                   };
25881                 }]);
25882             </file>
25883             <file name="protractor.js" type="protractor">
25884               var model = element(by.binding('user.name'));
25885               var input = element(by.model('user.name'));
25886               var other = element(by.model('user.data'));
25887
25888               it('should allow custom events', function() {
25889                 input.sendKeys(' Doe');
25890                 input.click();
25891                 expect(model.getText()).toEqual('John');
25892                 other.click();
25893                 expect(model.getText()).toEqual('John Doe');
25894               });
25895
25896               it('should $rollbackViewValue when model changes', function() {
25897                 input.sendKeys(' Doe');
25898                 expect(input.getAttribute('value')).toEqual('John Doe');
25899                 input.sendKeys(protractor.Key.ESCAPE);
25900                 expect(input.getAttribute('value')).toEqual('John');
25901                 other.click();
25902                 expect(model.getText()).toEqual('John');
25903               });
25904             </file>
25905           </example>
25906
25907           This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25908           If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25909
25910           <example name="ngModelOptions-directive-debounce" module="optionsExample">
25911             <file name="index.html">
25912               <div ng-controller="ExampleController">
25913                 <form name="userForm">
25914                   <label>Name:
25915                     <input type="text" name="userName"
25916                            ng-model="user.name"
25917                            ng-model-options="{ debounce: 1000 }" />
25918                   </label>
25919                   <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25920                   <br />
25921                 </form>
25922                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25923               </div>
25924             </file>
25925             <file name="app.js">
25926               angular.module('optionsExample', [])
25927                 .controller('ExampleController', ['$scope', function($scope) {
25928                   $scope.user = { name: 'Igor' };
25929                 }]);
25930             </file>
25931           </example>
25932
25933           This one shows how to bind to getter/setters:
25934
25935           <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25936             <file name="index.html">
25937               <div ng-controller="ExampleController">
25938                 <form name="userForm">
25939                   <label>Name:
25940                     <input type="text" name="userName"
25941                            ng-model="user.name"
25942                            ng-model-options="{ getterSetter: true }" />
25943                   </label>
25944                 </form>
25945                 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25946               </div>
25947             </file>
25948             <file name="app.js">
25949               angular.module('getterSetterExample', [])
25950                 .controller('ExampleController', ['$scope', function($scope) {
25951                   var _name = 'Brian';
25952                   $scope.user = {
25953                     name: function(newName) {
25954                       // Note that newName can be undefined for two reasons:
25955                       // 1. Because it is called as a getter and thus called with no arguments
25956                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25957                       //    input is invalid
25958                       return arguments.length ? (_name = newName) : _name;
25959                     }
25960                   };
25961                 }]);
25962             </file>
25963           </example>
25964          */
25965         var ngModelOptionsDirective = function() {
25966           return {
25967             restrict: 'A',
25968             controller: ['$scope', '$attrs', function($scope, $attrs) {
25969               var that = this;
25970               this.$options = copy($scope.$eval($attrs.ngModelOptions));
25971               // Allow adding/overriding bound events
25972               if (isDefined(this.$options.updateOn)) {
25973                 this.$options.updateOnDefault = false;
25974                 // extract "default" pseudo-event from list of events that can trigger a model update
25975                 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25976                   that.$options.updateOnDefault = true;
25977                   return ' ';
25978                 }));
25979               } else {
25980                 this.$options.updateOnDefault = true;
25981               }
25982             }]
25983           };
25984         };
25985
25986
25987
25988         // helper methods
25989         function addSetValidityMethod(context) {
25990           var ctrl = context.ctrl,
25991               $element = context.$element,
25992               classCache = {},
25993               set = context.set,
25994               unset = context.unset,
25995               $animate = context.$animate;
25996
25997           classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25998
25999           ctrl.$setValidity = setValidity;
26000
26001           function setValidity(validationErrorKey, state, controller) {
26002             if (isUndefined(state)) {
26003               createAndSet('$pending', validationErrorKey, controller);
26004             } else {
26005               unsetAndCleanup('$pending', validationErrorKey, controller);
26006             }
26007             if (!isBoolean(state)) {
26008               unset(ctrl.$error, validationErrorKey, controller);
26009               unset(ctrl.$$success, validationErrorKey, controller);
26010             } else {
26011               if (state) {
26012                 unset(ctrl.$error, validationErrorKey, controller);
26013                 set(ctrl.$$success, validationErrorKey, controller);
26014               } else {
26015                 set(ctrl.$error, validationErrorKey, controller);
26016                 unset(ctrl.$$success, validationErrorKey, controller);
26017               }
26018             }
26019             if (ctrl.$pending) {
26020               cachedToggleClass(PENDING_CLASS, true);
26021               ctrl.$valid = ctrl.$invalid = undefined;
26022               toggleValidationCss('', null);
26023             } else {
26024               cachedToggleClass(PENDING_CLASS, false);
26025               ctrl.$valid = isObjectEmpty(ctrl.$error);
26026               ctrl.$invalid = !ctrl.$valid;
26027               toggleValidationCss('', ctrl.$valid);
26028             }
26029
26030             // re-read the state as the set/unset methods could have
26031             // combined state in ctrl.$error[validationError] (used for forms),
26032             // where setting/unsetting only increments/decrements the value,
26033             // and does not replace it.
26034             var combinedState;
26035             if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26036               combinedState = undefined;
26037             } else if (ctrl.$error[validationErrorKey]) {
26038               combinedState = false;
26039             } else if (ctrl.$$success[validationErrorKey]) {
26040               combinedState = true;
26041             } else {
26042               combinedState = null;
26043             }
26044
26045             toggleValidationCss(validationErrorKey, combinedState);
26046             ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26047           }
26048
26049           function createAndSet(name, value, controller) {
26050             if (!ctrl[name]) {
26051               ctrl[name] = {};
26052             }
26053             set(ctrl[name], value, controller);
26054           }
26055
26056           function unsetAndCleanup(name, value, controller) {
26057             if (ctrl[name]) {
26058               unset(ctrl[name], value, controller);
26059             }
26060             if (isObjectEmpty(ctrl[name])) {
26061               ctrl[name] = undefined;
26062             }
26063           }
26064
26065           function cachedToggleClass(className, switchValue) {
26066             if (switchValue && !classCache[className]) {
26067               $animate.addClass($element, className);
26068               classCache[className] = true;
26069             } else if (!switchValue && classCache[className]) {
26070               $animate.removeClass($element, className);
26071               classCache[className] = false;
26072             }
26073           }
26074
26075           function toggleValidationCss(validationErrorKey, isValid) {
26076             validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26077
26078             cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26079             cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26080           }
26081         }
26082
26083         function isObjectEmpty(obj) {
26084           if (obj) {
26085             for (var prop in obj) {
26086               if (obj.hasOwnProperty(prop)) {
26087                 return false;
26088               }
26089             }
26090           }
26091           return true;
26092         }
26093
26094         /**
26095          * @ngdoc directive
26096          * @name ngNonBindable
26097          * @restrict AC
26098          * @priority 1000
26099          *
26100          * @description
26101          * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26102          * DOM element. This is useful if the element contains what appears to be Angular directives and
26103          * bindings but which should be ignored by Angular. This could be the case if you have a site that
26104          * displays snippets of code, for instance.
26105          *
26106          * @element ANY
26107          *
26108          * @example
26109          * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26110          * but the one wrapped in `ngNonBindable` is left alone.
26111          *
26112          * @example
26113             <example>
26114               <file name="index.html">
26115                 <div>Normal: {{1 + 2}}</div>
26116                 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26117               </file>
26118               <file name="protractor.js" type="protractor">
26119                it('should check ng-non-bindable', function() {
26120                  expect(element(by.binding('1 + 2')).getText()).toContain('3');
26121                  expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26122                });
26123               </file>
26124             </example>
26125          */
26126         var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26127
26128         /* global jqLiteRemove */
26129
26130         var ngOptionsMinErr = minErr('ngOptions');
26131
26132         /**
26133          * @ngdoc directive
26134          * @name ngOptions
26135          * @restrict A
26136          *
26137          * @description
26138          *
26139          * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26140          * elements for the `<select>` element using the array or object obtained by evaluating the
26141          * `ngOptions` comprehension expression.
26142          *
26143          * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26144          * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26145          * increasing speed by not creating a new scope for each repeated instance, as well as providing
26146          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26147          * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26148          *  to a non-string value. This is because an option element can only be bound to string values at
26149          * present.
26150          *
26151          * When an item in the `<select>` menu is selected, the array element or object property
26152          * represented by the selected option will be bound to the model identified by the `ngModel`
26153          * directive.
26154          *
26155          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26156          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26157          * option. See example below for demonstration.
26158          *
26159          * ## Complex Models (objects or collections)
26160          *
26161          * By default, `ngModel` watches the model by reference, not value. This is important to know when
26162          * binding the select to a model that is an object or a collection.
26163          *
26164          * One issue occurs if you want to preselect an option. For example, if you set
26165          * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26166          * because the objects are not identical. So by default, you should always reference the item in your collection
26167          * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26168          *
26169          * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26170          * of the item not by reference, but by the result of the `track by` expression. For example, if your
26171          * collection items have an id property, you would `track by item.id`.
26172          *
26173          * A different issue with objects or collections is that ngModel won't detect if an object property or
26174          * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26175          * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26176          * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26177          * has not changed identity, but only a property on the object or an item in the collection changes.
26178          *
26179          * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26180          * if the model is an array). This means that changing a property deeper than the first level inside the
26181          * object/collection will not trigger a re-rendering.
26182          *
26183          * ## `select` **`as`**
26184          *
26185          * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26186          * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26187          * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26188          * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26189          *
26190          *
26191          * ### `select` **`as`** and **`track by`**
26192          *
26193          * <div class="alert alert-warning">
26194          * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26195          * </div>
26196          *
26197          * Given this array of items on the $scope:
26198          *
26199          * ```js
26200          * $scope.items = [{
26201          *   id: 1,
26202          *   label: 'aLabel',
26203          *   subItem: { name: 'aSubItem' }
26204          * }, {
26205          *   id: 2,
26206          *   label: 'bLabel',
26207          *   subItem: { name: 'bSubItem' }
26208          * }];
26209          * ```
26210          *
26211          * This will work:
26212          *
26213          * ```html
26214          * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26215          * ```
26216          * ```js
26217          * $scope.selected = $scope.items[0];
26218          * ```
26219          *
26220          * but this will not work:
26221          *
26222          * ```html
26223          * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26224          * ```
26225          * ```js
26226          * $scope.selected = $scope.items[0].subItem;
26227          * ```
26228          *
26229          * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26230          * `items` array. Because the selected option has been set programmatically in the controller, the
26231          * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26232          * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26233          * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26234          * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26235          * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26236          *
26237          *
26238          * @param {string} ngModel Assignable angular expression to data-bind to.
26239          * @param {string=} name Property name of the form under which the control is published.
26240          * @param {string=} required The control is considered valid only if value is entered.
26241          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26242          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26243          *    `required` when you want to data-bind to the `required` attribute.
26244          * @param {comprehension_expression=} ngOptions in one of the following forms:
26245          *
26246          *   * for array data sources:
26247          *     * `label` **`for`** `value` **`in`** `array`
26248          *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26249          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26250          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26251          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26252          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26253          *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26254          *        (for including a filter with `track by`)
26255          *   * for object data sources:
26256          *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26257          *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26258          *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26259          *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26260          *     * `select` **`as`** `label` **`group by`** `group`
26261          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26262          *     * `select` **`as`** `label` **`disable when`** `disable`
26263          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26264          *
26265          * Where:
26266          *
26267          *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26268          *   * `value`: local variable which will refer to each item in the `array` or each property value
26269          *      of `object` during iteration.
26270          *   * `key`: local variable which will refer to a property name in `object` during iteration.
26271          *   * `label`: The result of this expression will be the label for `<option>` element. The
26272          *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26273          *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26274          *      element. If not specified, `select` expression will default to `value`.
26275          *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26276          *      DOM element.
26277          *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26278          *      element. Return `true` to disable.
26279          *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26280          *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26281          *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26282          *      even when the options are recreated (e.g. reloaded from the server).
26283          *
26284          * @example
26285             <example module="selectExample">
26286               <file name="index.html">
26287                 <script>
26288                 angular.module('selectExample', [])
26289                   .controller('ExampleController', ['$scope', function($scope) {
26290                     $scope.colors = [
26291                       {name:'black', shade:'dark'},
26292                       {name:'white', shade:'light', notAnOption: true},
26293                       {name:'red', shade:'dark'},
26294                       {name:'blue', shade:'dark', notAnOption: true},
26295                       {name:'yellow', shade:'light', notAnOption: false}
26296                     ];
26297                     $scope.myColor = $scope.colors[2]; // red
26298                   }]);
26299                 </script>
26300                 <div ng-controller="ExampleController">
26301                   <ul>
26302                     <li ng-repeat="color in colors">
26303                       <label>Name: <input ng-model="color.name"></label>
26304                       <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26305                       <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26306                     </li>
26307                     <li>
26308                       <button ng-click="colors.push({})">add</button>
26309                     </li>
26310                   </ul>
26311                   <hr/>
26312                   <label>Color (null not allowed):
26313                     <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26314                   </label><br/>
26315                   <label>Color (null allowed):
26316                   <span  class="nullable">
26317                     <select ng-model="myColor" ng-options="color.name for color in colors">
26318                       <option value="">-- choose color --</option>
26319                     </select>
26320                   </span></label><br/>
26321
26322                   <label>Color grouped by shade:
26323                     <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26324                     </select>
26325                   </label><br/>
26326
26327                   <label>Color grouped by shade, with some disabled:
26328                     <select ng-model="myColor"
26329                           ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26330                     </select>
26331                   </label><br/>
26332
26333
26334
26335                   Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26336                   <br/>
26337                   <hr/>
26338                   Currently selected: {{ {selected_color:myColor} }}
26339                   <div style="border:solid 1px black; height:20px"
26340                        ng-style="{'background-color':myColor.name}">
26341                   </div>
26342                 </div>
26343               </file>
26344               <file name="protractor.js" type="protractor">
26345                  it('should check ng-options', function() {
26346                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26347                    element.all(by.model('myColor')).first().click();
26348                    element.all(by.css('select[ng-model="myColor"] option')).first().click();
26349                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26350                    element(by.css('.nullable select[ng-model="myColor"]')).click();
26351                    element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26352                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26353                  });
26354               </file>
26355             </example>
26356          */
26357
26358         // jshint maxlen: false
26359         //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26360         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]+?))?$/;
26361                                 // 1: value expression (valueFn)
26362                                 // 2: label expression (displayFn)
26363                                 // 3: group by expression (groupByFn)
26364                                 // 4: disable when expression (disableWhenFn)
26365                                 // 5: array item variable name
26366                                 // 6: object item key variable name
26367                                 // 7: object item value variable name
26368                                 // 8: collection expression
26369                                 // 9: track by expression
26370         // jshint maxlen: 100
26371
26372
26373         var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26374
26375           function parseOptionsExpression(optionsExp, selectElement, scope) {
26376
26377             var match = optionsExp.match(NG_OPTIONS_REGEXP);
26378             if (!(match)) {
26379               throw ngOptionsMinErr('iexp',
26380                 "Expected expression in form of " +
26381                 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26382                 " but got '{0}'. Element: {1}",
26383                 optionsExp, startingTag(selectElement));
26384             }
26385
26386             // Extract the parts from the ngOptions expression
26387
26388             // The variable name for the value of the item in the collection
26389             var valueName = match[5] || match[7];
26390             // The variable name for the key of the item in the collection
26391             var keyName = match[6];
26392
26393             // An expression that generates the viewValue for an option if there is a label expression
26394             var selectAs = / as /.test(match[0]) && match[1];
26395             // An expression that is used to track the id of each object in the options collection
26396             var trackBy = match[9];
26397             // An expression that generates the viewValue for an option if there is no label expression
26398             var valueFn = $parse(match[2] ? match[1] : valueName);
26399             var selectAsFn = selectAs && $parse(selectAs);
26400             var viewValueFn = selectAsFn || valueFn;
26401             var trackByFn = trackBy && $parse(trackBy);
26402
26403             // Get the value by which we are going to track the option
26404             // if we have a trackFn then use that (passing scope and locals)
26405             // otherwise just hash the given viewValue
26406             var getTrackByValueFn = trackBy ?
26407                                       function(value, locals) { return trackByFn(scope, locals); } :
26408                                       function getHashOfValue(value) { return hashKey(value); };
26409             var getTrackByValue = function(value, key) {
26410               return getTrackByValueFn(value, getLocals(value, key));
26411             };
26412
26413             var displayFn = $parse(match[2] || match[1]);
26414             var groupByFn = $parse(match[3] || '');
26415             var disableWhenFn = $parse(match[4] || '');
26416             var valuesFn = $parse(match[8]);
26417
26418             var locals = {};
26419             var getLocals = keyName ? function(value, key) {
26420               locals[keyName] = key;
26421               locals[valueName] = value;
26422               return locals;
26423             } : function(value) {
26424               locals[valueName] = value;
26425               return locals;
26426             };
26427
26428
26429             function Option(selectValue, viewValue, label, group, disabled) {
26430               this.selectValue = selectValue;
26431               this.viewValue = viewValue;
26432               this.label = label;
26433               this.group = group;
26434               this.disabled = disabled;
26435             }
26436
26437             function getOptionValuesKeys(optionValues) {
26438               var optionValuesKeys;
26439
26440               if (!keyName && isArrayLike(optionValues)) {
26441                 optionValuesKeys = optionValues;
26442               } else {
26443                 // if object, extract keys, in enumeration order, unsorted
26444                 optionValuesKeys = [];
26445                 for (var itemKey in optionValues) {
26446                   if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26447                     optionValuesKeys.push(itemKey);
26448                   }
26449                 }
26450               }
26451               return optionValuesKeys;
26452             }
26453
26454             return {
26455               trackBy: trackBy,
26456               getTrackByValue: getTrackByValue,
26457               getWatchables: $parse(valuesFn, function(optionValues) {
26458                 // Create a collection of things that we would like to watch (watchedArray)
26459                 // so that they can all be watched using a single $watchCollection
26460                 // that only runs the handler once if anything changes
26461                 var watchedArray = [];
26462                 optionValues = optionValues || [];
26463
26464                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26465                 var optionValuesLength = optionValuesKeys.length;
26466                 for (var index = 0; index < optionValuesLength; index++) {
26467                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26468                   var value = optionValues[key];
26469
26470                   var locals = getLocals(optionValues[key], key);
26471                   var selectValue = getTrackByValueFn(optionValues[key], locals);
26472                   watchedArray.push(selectValue);
26473
26474                   // Only need to watch the displayFn if there is a specific label expression
26475                   if (match[2] || match[1]) {
26476                     var label = displayFn(scope, locals);
26477                     watchedArray.push(label);
26478                   }
26479
26480                   // Only need to watch the disableWhenFn if there is a specific disable expression
26481                   if (match[4]) {
26482                     var disableWhen = disableWhenFn(scope, locals);
26483                     watchedArray.push(disableWhen);
26484                   }
26485                 }
26486                 return watchedArray;
26487               }),
26488
26489               getOptions: function() {
26490
26491                 var optionItems = [];
26492                 var selectValueMap = {};
26493
26494                 // The option values were already computed in the `getWatchables` fn,
26495                 // which must have been called to trigger `getOptions`
26496                 var optionValues = valuesFn(scope) || [];
26497                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26498                 var optionValuesLength = optionValuesKeys.length;
26499
26500                 for (var index = 0; index < optionValuesLength; index++) {
26501                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26502                   var value = optionValues[key];
26503                   var locals = getLocals(value, key);
26504                   var viewValue = viewValueFn(scope, locals);
26505                   var selectValue = getTrackByValueFn(viewValue, locals);
26506                   var label = displayFn(scope, locals);
26507                   var group = groupByFn(scope, locals);
26508                   var disabled = disableWhenFn(scope, locals);
26509                   var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26510
26511                   optionItems.push(optionItem);
26512                   selectValueMap[selectValue] = optionItem;
26513                 }
26514
26515                 return {
26516                   items: optionItems,
26517                   selectValueMap: selectValueMap,
26518                   getOptionFromViewValue: function(value) {
26519                     return selectValueMap[getTrackByValue(value)];
26520                   },
26521                   getViewValueFromOption: function(option) {
26522                     // If the viewValue could be an object that may be mutated by the application,
26523                     // we need to make a copy and not return the reference to the value on the option.
26524                     return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26525                   }
26526                 };
26527               }
26528             };
26529           }
26530
26531
26532           // we can't just jqLite('<option>') since jqLite is not smart enough
26533           // to create it in <select> and IE barfs otherwise.
26534           var optionTemplate = document.createElement('option'),
26535               optGroupTemplate = document.createElement('optgroup');
26536
26537
26538             function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26539
26540               // if ngModel is not defined, we don't need to do anything
26541               var ngModelCtrl = ctrls[1];
26542               if (!ngModelCtrl) return;
26543
26544               var selectCtrl = ctrls[0];
26545               var multiple = attr.multiple;
26546
26547               // The emptyOption allows the application developer to provide their own custom "empty"
26548               // option when the viewValue does not match any of the option values.
26549               var emptyOption;
26550               for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26551                 if (children[i].value === '') {
26552                   emptyOption = children.eq(i);
26553                   break;
26554                 }
26555               }
26556
26557               var providedEmptyOption = !!emptyOption;
26558
26559               var unknownOption = jqLite(optionTemplate.cloneNode(false));
26560               unknownOption.val('?');
26561
26562               var options;
26563               var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26564
26565
26566               var renderEmptyOption = function() {
26567                 if (!providedEmptyOption) {
26568                   selectElement.prepend(emptyOption);
26569                 }
26570                 selectElement.val('');
26571                 emptyOption.prop('selected', true); // needed for IE
26572                 emptyOption.attr('selected', true);
26573               };
26574
26575               var removeEmptyOption = function() {
26576                 if (!providedEmptyOption) {
26577                   emptyOption.remove();
26578                 }
26579               };
26580
26581
26582               var renderUnknownOption = function() {
26583                 selectElement.prepend(unknownOption);
26584                 selectElement.val('?');
26585                 unknownOption.prop('selected', true); // needed for IE
26586                 unknownOption.attr('selected', true);
26587               };
26588
26589               var removeUnknownOption = function() {
26590                 unknownOption.remove();
26591               };
26592
26593               // Update the controller methods for multiple selectable options
26594               if (!multiple) {
26595
26596                 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26597                   var option = options.getOptionFromViewValue(value);
26598
26599                   if (option && !option.disabled) {
26600                     if (selectElement[0].value !== option.selectValue) {
26601                       removeUnknownOption();
26602                       removeEmptyOption();
26603
26604                       selectElement[0].value = option.selectValue;
26605                       option.element.selected = true;
26606                       option.element.setAttribute('selected', 'selected');
26607                     }
26608                   } else {
26609                     if (value === null || providedEmptyOption) {
26610                       removeUnknownOption();
26611                       renderEmptyOption();
26612                     } else {
26613                       removeEmptyOption();
26614                       renderUnknownOption();
26615                     }
26616                   }
26617                 };
26618
26619                 selectCtrl.readValue = function readNgOptionsValue() {
26620
26621                   var selectedOption = options.selectValueMap[selectElement.val()];
26622
26623                   if (selectedOption && !selectedOption.disabled) {
26624                     removeEmptyOption();
26625                     removeUnknownOption();
26626                     return options.getViewValueFromOption(selectedOption);
26627                   }
26628                   return null;
26629                 };
26630
26631                 // If we are using `track by` then we must watch the tracked value on the model
26632                 // since ngModel only watches for object identity change
26633                 if (ngOptions.trackBy) {
26634                   scope.$watch(
26635                     function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26636                     function() { ngModelCtrl.$render(); }
26637                   );
26638                 }
26639
26640               } else {
26641
26642                 ngModelCtrl.$isEmpty = function(value) {
26643                   return !value || value.length === 0;
26644                 };
26645
26646
26647                 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26648                   options.items.forEach(function(option) {
26649                     option.element.selected = false;
26650                   });
26651
26652                   if (value) {
26653                     value.forEach(function(item) {
26654                       var option = options.getOptionFromViewValue(item);
26655                       if (option && !option.disabled) option.element.selected = true;
26656                     });
26657                   }
26658                 };
26659
26660
26661                 selectCtrl.readValue = function readNgOptionsMultiple() {
26662                   var selectedValues = selectElement.val() || [],
26663                       selections = [];
26664
26665                   forEach(selectedValues, function(value) {
26666                     var option = options.selectValueMap[value];
26667                     if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26668                   });
26669
26670                   return selections;
26671                 };
26672
26673                 // If we are using `track by` then we must watch these tracked values on the model
26674                 // since ngModel only watches for object identity change
26675                 if (ngOptions.trackBy) {
26676
26677                   scope.$watchCollection(function() {
26678                     if (isArray(ngModelCtrl.$viewValue)) {
26679                       return ngModelCtrl.$viewValue.map(function(value) {
26680                         return ngOptions.getTrackByValue(value);
26681                       });
26682                     }
26683                   }, function() {
26684                     ngModelCtrl.$render();
26685                   });
26686
26687                 }
26688               }
26689
26690
26691               if (providedEmptyOption) {
26692
26693                 // we need to remove it before calling selectElement.empty() because otherwise IE will
26694                 // remove the label from the element. wtf?
26695                 emptyOption.remove();
26696
26697                 // compile the element since there might be bindings in it
26698                 $compile(emptyOption)(scope);
26699
26700                 // remove the class, which is added automatically because we recompile the element and it
26701                 // becomes the compilation root
26702                 emptyOption.removeClass('ng-scope');
26703               } else {
26704                 emptyOption = jqLite(optionTemplate.cloneNode(false));
26705               }
26706
26707               // We need to do this here to ensure that the options object is defined
26708               // when we first hit it in writeNgOptionsValue
26709               updateOptions();
26710
26711               // We will re-render the option elements if the option values or labels change
26712               scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26713
26714               // ------------------------------------------------------------------ //
26715
26716
26717               function updateOptionElement(option, element) {
26718                 option.element = element;
26719                 element.disabled = option.disabled;
26720                 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26721                 // selects in certain circumstances when multiple selects are next to each other and display
26722                 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26723                 // See https://github.com/angular/angular.js/issues/11314 for more info.
26724                 // This is unfortunately untestable with unit / e2e tests
26725                 if (option.label !== element.label) {
26726                   element.label = option.label;
26727                   element.textContent = option.label;
26728                 }
26729                 if (option.value !== element.value) element.value = option.selectValue;
26730               }
26731
26732               function addOrReuseElement(parent, current, type, templateElement) {
26733                 var element;
26734                 // Check whether we can reuse the next element
26735                 if (current && lowercase(current.nodeName) === type) {
26736                   // The next element is the right type so reuse it
26737                   element = current;
26738                 } else {
26739                   // The next element is not the right type so create a new one
26740                   element = templateElement.cloneNode(false);
26741                   if (!current) {
26742                     // There are no more elements so just append it to the select
26743                     parent.appendChild(element);
26744                   } else {
26745                     // The next element is not a group so insert the new one
26746                     parent.insertBefore(element, current);
26747                   }
26748                 }
26749                 return element;
26750               }
26751
26752
26753               function removeExcessElements(current) {
26754                 var next;
26755                 while (current) {
26756                   next = current.nextSibling;
26757                   jqLiteRemove(current);
26758                   current = next;
26759                 }
26760               }
26761
26762
26763               function skipEmptyAndUnknownOptions(current) {
26764                 var emptyOption_ = emptyOption && emptyOption[0];
26765                 var unknownOption_ = unknownOption && unknownOption[0];
26766
26767                 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26768                 // because the compiled empty option might have been replaced by a comment because
26769                 // it had an "element" transclusion directive on it (such as ngIf)
26770                 if (emptyOption_ || unknownOption_) {
26771                   while (current &&
26772                         (current === emptyOption_ ||
26773                         current === unknownOption_ ||
26774                         current.nodeType === NODE_TYPE_COMMENT ||
26775                         current.value === '')) {
26776                     current = current.nextSibling;
26777                   }
26778                 }
26779                 return current;
26780               }
26781
26782
26783               function updateOptions() {
26784
26785                 var previousValue = options && selectCtrl.readValue();
26786
26787                 options = ngOptions.getOptions();
26788
26789                 var groupMap = {};
26790                 var currentElement = selectElement[0].firstChild;
26791
26792                 // Ensure that the empty option is always there if it was explicitly provided
26793                 if (providedEmptyOption) {
26794                   selectElement.prepend(emptyOption);
26795                 }
26796
26797                 currentElement = skipEmptyAndUnknownOptions(currentElement);
26798
26799                 options.items.forEach(function updateOption(option) {
26800                   var group;
26801                   var groupElement;
26802                   var optionElement;
26803
26804                   if (option.group) {
26805
26806                     // This option is to live in a group
26807                     // See if we have already created this group
26808                     group = groupMap[option.group];
26809
26810                     if (!group) {
26811
26812                       // We have not already created this group
26813                       groupElement = addOrReuseElement(selectElement[0],
26814                                                        currentElement,
26815                                                        'optgroup',
26816                                                        optGroupTemplate);
26817                       // Move to the next element
26818                       currentElement = groupElement.nextSibling;
26819
26820                       // Update the label on the group element
26821                       groupElement.label = option.group;
26822
26823                       // Store it for use later
26824                       group = groupMap[option.group] = {
26825                         groupElement: groupElement,
26826                         currentOptionElement: groupElement.firstChild
26827                       };
26828
26829                     }
26830
26831                     // So now we have a group for this option we add the option to the group
26832                     optionElement = addOrReuseElement(group.groupElement,
26833                                                       group.currentOptionElement,
26834                                                       'option',
26835                                                       optionTemplate);
26836                     updateOptionElement(option, optionElement);
26837                     // Move to the next element
26838                     group.currentOptionElement = optionElement.nextSibling;
26839
26840                   } else {
26841
26842                     // This option is not in a group
26843                     optionElement = addOrReuseElement(selectElement[0],
26844                                                       currentElement,
26845                                                       'option',
26846                                                       optionTemplate);
26847                     updateOptionElement(option, optionElement);
26848                     // Move to the next element
26849                     currentElement = optionElement.nextSibling;
26850                   }
26851                 });
26852
26853
26854                 // Now remove all excess options and group
26855                 Object.keys(groupMap).forEach(function(key) {
26856                   removeExcessElements(groupMap[key].currentOptionElement);
26857                 });
26858                 removeExcessElements(currentElement);
26859
26860                 ngModelCtrl.$render();
26861
26862                 // Check to see if the value has changed due to the update to the options
26863                 if (!ngModelCtrl.$isEmpty(previousValue)) {
26864                   var nextValue = selectCtrl.readValue();
26865                   if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26866                     ngModelCtrl.$setViewValue(nextValue);
26867                     ngModelCtrl.$render();
26868                   }
26869                 }
26870
26871               }
26872           }
26873
26874           return {
26875             restrict: 'A',
26876             terminal: true,
26877             require: ['select', '?ngModel'],
26878             link: {
26879               pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26880                 // Deactivate the SelectController.register method to prevent
26881                 // option directives from accidentally registering themselves
26882                 // (and unwanted $destroy handlers etc.)
26883                 ctrls[0].registerOption = noop;
26884               },
26885               post: ngOptionsPostLink
26886             }
26887           };
26888         }];
26889
26890         /**
26891          * @ngdoc directive
26892          * @name ngPluralize
26893          * @restrict EA
26894          *
26895          * @description
26896          * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26897          * These rules are bundled with angular.js, but can be overridden
26898          * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26899          * by specifying the mappings between
26900          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26901          * and the strings to be displayed.
26902          *
26903          * # Plural categories and explicit number rules
26904          * There are two
26905          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26906          * in Angular's default en-US locale: "one" and "other".
26907          *
26908          * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26909          * any number that is not 1), an explicit number rule can only match one number. For example, the
26910          * explicit number rule for "3" matches the number 3. There are examples of plural categories
26911          * and explicit number rules throughout the rest of this documentation.
26912          *
26913          * # Configuring ngPluralize
26914          * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26915          * You can also provide an optional attribute, `offset`.
26916          *
26917          * The value of the `count` attribute can be either a string or an {@link guide/expression
26918          * Angular expression}; these are evaluated on the current scope for its bound value.
26919          *
26920          * The `when` attribute specifies the mappings between plural categories and the actual
26921          * string to be displayed. The value of the attribute should be a JSON object.
26922          *
26923          * The following example shows how to configure ngPluralize:
26924          *
26925          * ```html
26926          * <ng-pluralize count="personCount"
26927                          when="{'0': 'Nobody is viewing.',
26928          *                      'one': '1 person is viewing.',
26929          *                      'other': '{} people are viewing.'}">
26930          * </ng-pluralize>
26931          *```
26932          *
26933          * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26934          * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26935          * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26936          * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26937          * show "a dozen people are viewing".
26938          *
26939          * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26940          * into pluralized strings. In the previous example, Angular will replace `{}` with
26941          * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26942          * for <span ng-non-bindable>{{numberExpression}}</span>.
26943          *
26944          * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26945          * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26946          *
26947          * # Configuring ngPluralize with offset
26948          * The `offset` attribute allows further customization of pluralized text, which can result in
26949          * a better user experience. For example, instead of the message "4 people are viewing this document",
26950          * you might display "John, Kate and 2 others are viewing this document".
26951          * The offset attribute allows you to offset a number by any desired value.
26952          * Let's take a look at an example:
26953          *
26954          * ```html
26955          * <ng-pluralize count="personCount" offset=2
26956          *               when="{'0': 'Nobody is viewing.',
26957          *                      '1': '{{person1}} is viewing.',
26958          *                      '2': '{{person1}} and {{person2}} are viewing.',
26959          *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26960          *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26961          * </ng-pluralize>
26962          * ```
26963          *
26964          * Notice that we are still using two plural categories(one, other), but we added
26965          * three explicit number rules 0, 1 and 2.
26966          * When one person, perhaps John, views the document, "John is viewing" will be shown.
26967          * When three people view the document, no explicit number rule is found, so
26968          * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26969          * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26970          * is shown.
26971          *
26972          * Note that when you specify offsets, you must provide explicit number rules for
26973          * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26974          * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26975          * plural categories "one" and "other".
26976          *
26977          * @param {string|expression} count The variable to be bound to.
26978          * @param {string} when The mapping between plural category to its corresponding strings.
26979          * @param {number=} offset Offset to deduct from the total number.
26980          *
26981          * @example
26982             <example module="pluralizeExample">
26983               <file name="index.html">
26984                 <script>
26985                   angular.module('pluralizeExample', [])
26986                     .controller('ExampleController', ['$scope', function($scope) {
26987                       $scope.person1 = 'Igor';
26988                       $scope.person2 = 'Misko';
26989                       $scope.personCount = 1;
26990                     }]);
26991                 </script>
26992                 <div ng-controller="ExampleController">
26993                   <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26994                   <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26995                   <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26996
26997                   <!--- Example with simple pluralization rules for en locale --->
26998                   Without Offset:
26999                   <ng-pluralize count="personCount"
27000                                 when="{'0': 'Nobody is viewing.',
27001                                        'one': '1 person is viewing.',
27002                                        'other': '{} people are viewing.'}">
27003                   </ng-pluralize><br>
27004
27005                   <!--- Example with offset --->
27006                   With Offset(2):
27007                   <ng-pluralize count="personCount" offset=2
27008                                 when="{'0': 'Nobody is viewing.',
27009                                        '1': '{{person1}} is viewing.',
27010                                        '2': '{{person1}} and {{person2}} are viewing.',
27011                                        'one': '{{person1}}, {{person2}} and one other person are viewing.',
27012                                        'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27013                   </ng-pluralize>
27014                 </div>
27015               </file>
27016               <file name="protractor.js" type="protractor">
27017                 it('should show correct pluralized string', function() {
27018                   var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27019                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27020                   var countInput = element(by.model('personCount'));
27021
27022                   expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27023                   expect(withOffset.getText()).toEqual('Igor is viewing.');
27024
27025                   countInput.clear();
27026                   countInput.sendKeys('0');
27027
27028                   expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27029                   expect(withOffset.getText()).toEqual('Nobody is viewing.');
27030
27031                   countInput.clear();
27032                   countInput.sendKeys('2');
27033
27034                   expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27035                   expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27036
27037                   countInput.clear();
27038                   countInput.sendKeys('3');
27039
27040                   expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27041                   expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27042
27043                   countInput.clear();
27044                   countInput.sendKeys('4');
27045
27046                   expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27047                   expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27048                 });
27049                 it('should show data-bound names', function() {
27050                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27051                   var personCount = element(by.model('personCount'));
27052                   var person1 = element(by.model('person1'));
27053                   var person2 = element(by.model('person2'));
27054                   personCount.clear();
27055                   personCount.sendKeys('4');
27056                   person1.clear();
27057                   person1.sendKeys('Di');
27058                   person2.clear();
27059                   person2.sendKeys('Vojta');
27060                   expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27061                 });
27062               </file>
27063             </example>
27064          */
27065         var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27066           var BRACE = /{}/g,
27067               IS_WHEN = /^when(Minus)?(.+)$/;
27068
27069           return {
27070             link: function(scope, element, attr) {
27071               var numberExp = attr.count,
27072                   whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27073                   offset = attr.offset || 0,
27074                   whens = scope.$eval(whenExp) || {},
27075                   whensExpFns = {},
27076                   startSymbol = $interpolate.startSymbol(),
27077                   endSymbol = $interpolate.endSymbol(),
27078                   braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27079                   watchRemover = angular.noop,
27080                   lastCount;
27081
27082               forEach(attr, function(expression, attributeName) {
27083                 var tmpMatch = IS_WHEN.exec(attributeName);
27084                 if (tmpMatch) {
27085                   var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27086                   whens[whenKey] = element.attr(attr.$attr[attributeName]);
27087                 }
27088               });
27089               forEach(whens, function(expression, key) {
27090                 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27091
27092               });
27093
27094               scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27095                 var count = parseFloat(newVal);
27096                 var countIsNaN = isNaN(count);
27097
27098                 if (!countIsNaN && !(count in whens)) {
27099                   // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27100                   // Otherwise, check it against pluralization rules in $locale service.
27101                   count = $locale.pluralCat(count - offset);
27102                 }
27103
27104                 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27105                 // In JS `NaN !== NaN`, so we have to exlicitly check.
27106                 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27107                   watchRemover();
27108                   var whenExpFn = whensExpFns[count];
27109                   if (isUndefined(whenExpFn)) {
27110                     if (newVal != null) {
27111                       $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27112                     }
27113                     watchRemover = noop;
27114                     updateElementText();
27115                   } else {
27116                     watchRemover = scope.$watch(whenExpFn, updateElementText);
27117                   }
27118                   lastCount = count;
27119                 }
27120               });
27121
27122               function updateElementText(newText) {
27123                 element.text(newText || '');
27124               }
27125             }
27126           };
27127         }];
27128
27129         /**
27130          * @ngdoc directive
27131          * @name ngRepeat
27132          * @multiElement
27133          *
27134          * @description
27135          * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27136          * instance gets its own scope, where the given loop variable is set to the current collection item,
27137          * and `$index` is set to the item index or key.
27138          *
27139          * Special properties are exposed on the local scope of each template instance, including:
27140          *
27141          * | Variable  | Type            | Details                                                                     |
27142          * |-----------|-----------------|-----------------------------------------------------------------------------|
27143          * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27144          * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27145          * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27146          * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27147          * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27148          * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27149          *
27150          * <div class="alert alert-info">
27151          *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27152          *   This may be useful when, for instance, nesting ngRepeats.
27153          * </div>
27154          *
27155          *
27156          * # Iterating over object properties
27157          *
27158          * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27159          * syntax:
27160          *
27161          * ```js
27162          * <div ng-repeat="(key, value) in myObj"> ... </div>
27163          * ```
27164          *
27165          * You need to be aware that the JavaScript specification does not define the order of keys
27166          * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27167          * used to sort the keys alphabetically.)
27168          *
27169          * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27170          * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27171          * keys in the order in which they were defined, although there are exceptions when keys are deleted
27172          * 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).
27173          *
27174          * If this is not desired, the recommended workaround is to convert your object into an array
27175          * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27176          * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27177          * or implement a `$watch` on the object yourself.
27178          *
27179          *
27180          * # Tracking and Duplicates
27181          *
27182          * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27183          * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27184          *
27185          * * When an item is added, a new instance of the template is added to the DOM.
27186          * * When an item is removed, its template instance is removed from the DOM.
27187          * * When items are reordered, their respective templates are reordered in the DOM.
27188          *
27189          * To minimize creation of DOM elements, `ngRepeat` uses a function
27190          * to "keep track" of all items in the collection and their corresponding DOM elements.
27191          * For example, if an item is added to the collection, ngRepeat will know that all other items
27192          * already have DOM elements, and will not re-render them.
27193          *
27194          * The default tracking function (which tracks items by their identity) does not allow
27195          * duplicate items in arrays. This is because when there are duplicates, it is not possible
27196          * to maintain a one-to-one mapping between collection items and DOM elements.
27197          *
27198          * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27199          * with your own using the `track by` expression.
27200          *
27201          * For example, you may track items by the index of each item in the collection, using the
27202          * special scope property `$index`:
27203          * ```html
27204          *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27205          *      {{n}}
27206          *    </div>
27207          * ```
27208          *
27209          * You may also use arbitrary expressions in `track by`, including references to custom functions
27210          * on the scope:
27211          * ```html
27212          *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27213          *      {{n}}
27214          *    </div>
27215          * ```
27216          *
27217          * <div class="alert alert-success">
27218          * If you are working with objects that have an identifier property, you should track
27219          * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27220          * will not have to rebuild the DOM elements for items it has already rendered, even if the
27221          * JavaScript objects in the collection have been substituted for new ones. For large collections,
27222          * this signifincantly improves rendering performance. If you don't have a unique identifier,
27223          * `track by $index` can also provide a performance boost.
27224          * </div>
27225          * ```html
27226          *    <div ng-repeat="model in collection track by model.id">
27227          *      {{model.name}}
27228          *    </div>
27229          * ```
27230          *
27231          * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27232          * `$id` function, which tracks items by their identity:
27233          * ```html
27234          *    <div ng-repeat="obj in collection track by $id(obj)">
27235          *      {{obj.prop}}
27236          *    </div>
27237          * ```
27238          *
27239          * <div class="alert alert-warning">
27240          * **Note:** `track by` must always be the last expression:
27241          * </div>
27242          * ```
27243          * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27244          *     {{model.name}}
27245          * </div>
27246          * ```
27247          *
27248          * # Special repeat start and end points
27249          * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27250          * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27251          * 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)
27252          * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27253          *
27254          * The example below makes use of this feature:
27255          * ```html
27256          *   <header ng-repeat-start="item in items">
27257          *     Header {{ item }}
27258          *   </header>
27259          *   <div class="body">
27260          *     Body {{ item }}
27261          *   </div>
27262          *   <footer ng-repeat-end>
27263          *     Footer {{ item }}
27264          *   </footer>
27265          * ```
27266          *
27267          * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27268          * ```html
27269          *   <header>
27270          *     Header A
27271          *   </header>
27272          *   <div class="body">
27273          *     Body A
27274          *   </div>
27275          *   <footer>
27276          *     Footer A
27277          *   </footer>
27278          *   <header>
27279          *     Header B
27280          *   </header>
27281          *   <div class="body">
27282          *     Body B
27283          *   </div>
27284          *   <footer>
27285          *     Footer B
27286          *   </footer>
27287          * ```
27288          *
27289          * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27290          * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27291          *
27292          * @animations
27293          * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27294          *
27295          * **.leave** - when an item is removed from the list or when an item is filtered out
27296          *
27297          * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27298          *
27299          * @element ANY
27300          * @scope
27301          * @priority 1000
27302          * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27303          *   formats are currently supported:
27304          *
27305          *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27306          *     is a scope expression giving the collection to enumerate.
27307          *
27308          *     For example: `album in artist.albums`.
27309          *
27310          *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27311          *     and `expression` is the scope expression giving the collection to enumerate.
27312          *
27313          *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27314          *
27315          *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27316          *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27317          *     is specified, ng-repeat associates elements by identity. It is an error to have
27318          *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27319          *     mapped to the same DOM element, which is not possible.)
27320          *
27321          *     Note that the tracking expression must come last, after any filters, and the alias expression.
27322          *
27323          *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27324          *     will be associated by item identity in the array.
27325          *
27326          *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27327          *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27328          *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27329          *     element in the same way in the DOM.
27330          *
27331          *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27332          *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27333          *     property is same.
27334          *
27335          *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27336          *     to items in conjunction with a tracking expression.
27337          *
27338          *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27339          *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27340          *     when a filter is active on the repeater, but the filtered result set is empty.
27341          *
27342          *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27343          *     the items have been processed through the filter.
27344          *
27345          *     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
27346          *     (and not as operator, inside an expression).
27347          *
27348          *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27349          *
27350          * @example
27351          * This example initializes the scope to a list of names and
27352          * then uses `ngRepeat` to display every person:
27353           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27354             <file name="index.html">
27355               <div ng-init="friends = [
27356                 {name:'John', age:25, gender:'boy'},
27357                 {name:'Jessie', age:30, gender:'girl'},
27358                 {name:'Johanna', age:28, gender:'girl'},
27359                 {name:'Joy', age:15, gender:'girl'},
27360                 {name:'Mary', age:28, gender:'girl'},
27361                 {name:'Peter', age:95, gender:'boy'},
27362                 {name:'Sebastian', age:50, gender:'boy'},
27363                 {name:'Erika', age:27, gender:'girl'},
27364                 {name:'Patrick', age:40, gender:'boy'},
27365                 {name:'Samantha', age:60, gender:'girl'}
27366               ]">
27367                 I have {{friends.length}} friends. They are:
27368                 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27369                 <ul class="example-animate-container">
27370                   <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27371                     [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27372                   </li>
27373                   <li class="animate-repeat" ng-if="results.length == 0">
27374                     <strong>No results found...</strong>
27375                   </li>
27376                 </ul>
27377               </div>
27378             </file>
27379             <file name="animations.css">
27380               .example-animate-container {
27381                 background:white;
27382                 border:1px solid black;
27383                 list-style:none;
27384                 margin:0;
27385                 padding:0 10px;
27386               }
27387
27388               .animate-repeat {
27389                 line-height:40px;
27390                 list-style:none;
27391                 box-sizing:border-box;
27392               }
27393
27394               .animate-repeat.ng-move,
27395               .animate-repeat.ng-enter,
27396               .animate-repeat.ng-leave {
27397                 transition:all linear 0.5s;
27398               }
27399
27400               .animate-repeat.ng-leave.ng-leave-active,
27401               .animate-repeat.ng-move,
27402               .animate-repeat.ng-enter {
27403                 opacity:0;
27404                 max-height:0;
27405               }
27406
27407               .animate-repeat.ng-leave,
27408               .animate-repeat.ng-move.ng-move-active,
27409               .animate-repeat.ng-enter.ng-enter-active {
27410                 opacity:1;
27411                 max-height:40px;
27412               }
27413             </file>
27414             <file name="protractor.js" type="protractor">
27415               var friends = element.all(by.repeater('friend in friends'));
27416
27417               it('should render initial data set', function() {
27418                 expect(friends.count()).toBe(10);
27419                 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27420                 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27421                 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27422                 expect(element(by.binding('friends.length')).getText())
27423                     .toMatch("I have 10 friends. They are:");
27424               });
27425
27426                it('should update repeater when filter predicate changes', function() {
27427                  expect(friends.count()).toBe(10);
27428
27429                  element(by.model('q')).sendKeys('ma');
27430
27431                  expect(friends.count()).toBe(2);
27432                  expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27433                  expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27434                });
27435               </file>
27436             </example>
27437          */
27438         var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27439           var NG_REMOVED = '$$NG_REMOVED';
27440           var ngRepeatMinErr = minErr('ngRepeat');
27441
27442           var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27443             // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27444             scope[valueIdentifier] = value;
27445             if (keyIdentifier) scope[keyIdentifier] = key;
27446             scope.$index = index;
27447             scope.$first = (index === 0);
27448             scope.$last = (index === (arrayLength - 1));
27449             scope.$middle = !(scope.$first || scope.$last);
27450             // jshint bitwise: false
27451             scope.$odd = !(scope.$even = (index&1) === 0);
27452             // jshint bitwise: true
27453           };
27454
27455           var getBlockStart = function(block) {
27456             return block.clone[0];
27457           };
27458
27459           var getBlockEnd = function(block) {
27460             return block.clone[block.clone.length - 1];
27461           };
27462
27463
27464           return {
27465             restrict: 'A',
27466             multiElement: true,
27467             transclude: 'element',
27468             priority: 1000,
27469             terminal: true,
27470             $$tlb: true,
27471             compile: function ngRepeatCompile($element, $attr) {
27472               var expression = $attr.ngRepeat;
27473               var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27474
27475               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*$/);
27476
27477               if (!match) {
27478                 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27479                     expression);
27480               }
27481
27482               var lhs = match[1];
27483               var rhs = match[2];
27484               var aliasAs = match[3];
27485               var trackByExp = match[4];
27486
27487               match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27488
27489               if (!match) {
27490                 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27491                     lhs);
27492               }
27493               var valueIdentifier = match[3] || match[1];
27494               var keyIdentifier = match[2];
27495
27496               if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27497                   /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27498                 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27499                   aliasAs);
27500               }
27501
27502               var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27503               var hashFnLocals = {$id: hashKey};
27504
27505               if (trackByExp) {
27506                 trackByExpGetter = $parse(trackByExp);
27507               } else {
27508                 trackByIdArrayFn = function(key, value) {
27509                   return hashKey(value);
27510                 };
27511                 trackByIdObjFn = function(key) {
27512                   return key;
27513                 };
27514               }
27515
27516               return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27517
27518                 if (trackByExpGetter) {
27519                   trackByIdExpFn = function(key, value, index) {
27520                     // assign key, value, and $index to the locals so that they can be used in hash functions
27521                     if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27522                     hashFnLocals[valueIdentifier] = value;
27523                     hashFnLocals.$index = index;
27524                     return trackByExpGetter($scope, hashFnLocals);
27525                   };
27526                 }
27527
27528                 // Store a list of elements from previous run. This is a hash where key is the item from the
27529                 // iterator, and the value is objects with following properties.
27530                 //   - scope: bound scope
27531                 //   - element: previous element.
27532                 //   - index: position
27533                 //
27534                 // We are using no-proto object so that we don't need to guard against inherited props via
27535                 // hasOwnProperty.
27536                 var lastBlockMap = createMap();
27537
27538                 //watch props
27539                 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27540                   var index, length,
27541                       previousNode = $element[0],     // node that cloned nodes should be inserted after
27542                                                       // initialized to the comment node anchor
27543                       nextNode,
27544                       // Same as lastBlockMap but it has the current state. It will become the
27545                       // lastBlockMap on the next iteration.
27546                       nextBlockMap = createMap(),
27547                       collectionLength,
27548                       key, value, // key/value of iteration
27549                       trackById,
27550                       trackByIdFn,
27551                       collectionKeys,
27552                       block,       // last object information {scope, element, id}
27553                       nextBlockOrder,
27554                       elementsToRemove;
27555
27556                   if (aliasAs) {
27557                     $scope[aliasAs] = collection;
27558                   }
27559
27560                   if (isArrayLike(collection)) {
27561                     collectionKeys = collection;
27562                     trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27563                   } else {
27564                     trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27565                     // if object, extract keys, in enumeration order, unsorted
27566                     collectionKeys = [];
27567                     for (var itemKey in collection) {
27568                       if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27569                         collectionKeys.push(itemKey);
27570                       }
27571                     }
27572                   }
27573
27574                   collectionLength = collectionKeys.length;
27575                   nextBlockOrder = new Array(collectionLength);
27576
27577                   // locate existing items
27578                   for (index = 0; index < collectionLength; index++) {
27579                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27580                     value = collection[key];
27581                     trackById = trackByIdFn(key, value, index);
27582                     if (lastBlockMap[trackById]) {
27583                       // found previously seen block
27584                       block = lastBlockMap[trackById];
27585                       delete lastBlockMap[trackById];
27586                       nextBlockMap[trackById] = block;
27587                       nextBlockOrder[index] = block;
27588                     } else if (nextBlockMap[trackById]) {
27589                       // if collision detected. restore lastBlockMap and throw an error
27590                       forEach(nextBlockOrder, function(block) {
27591                         if (block && block.scope) lastBlockMap[block.id] = block;
27592                       });
27593                       throw ngRepeatMinErr('dupes',
27594                           "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27595                           expression, trackById, value);
27596                     } else {
27597                       // new never before seen block
27598                       nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27599                       nextBlockMap[trackById] = true;
27600                     }
27601                   }
27602
27603                   // remove leftover items
27604                   for (var blockKey in lastBlockMap) {
27605                     block = lastBlockMap[blockKey];
27606                     elementsToRemove = getBlockNodes(block.clone);
27607                     $animate.leave(elementsToRemove);
27608                     if (elementsToRemove[0].parentNode) {
27609                       // if the element was not removed yet because of pending animation, mark it as deleted
27610                       // so that we can ignore it later
27611                       for (index = 0, length = elementsToRemove.length; index < length; index++) {
27612                         elementsToRemove[index][NG_REMOVED] = true;
27613                       }
27614                     }
27615                     block.scope.$destroy();
27616                   }
27617
27618                   // we are not using forEach for perf reasons (trying to avoid #call)
27619                   for (index = 0; index < collectionLength; index++) {
27620                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27621                     value = collection[key];
27622                     block = nextBlockOrder[index];
27623
27624                     if (block.scope) {
27625                       // if we have already seen this object, then we need to reuse the
27626                       // associated scope/element
27627
27628                       nextNode = previousNode;
27629
27630                       // skip nodes that are already pending removal via leave animation
27631                       do {
27632                         nextNode = nextNode.nextSibling;
27633                       } while (nextNode && nextNode[NG_REMOVED]);
27634
27635                       if (getBlockStart(block) != nextNode) {
27636                         // existing item which got moved
27637                         $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27638                       }
27639                       previousNode = getBlockEnd(block);
27640                       updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27641                     } else {
27642                       // new item which we don't know about
27643                       $transclude(function ngRepeatTransclude(clone, scope) {
27644                         block.scope = scope;
27645                         // http://jsperf.com/clone-vs-createcomment
27646                         var endNode = ngRepeatEndComment.cloneNode(false);
27647                         clone[clone.length++] = endNode;
27648
27649                         // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27650                         $animate.enter(clone, null, jqLite(previousNode));
27651                         previousNode = endNode;
27652                         // Note: We only need the first/last node of the cloned nodes.
27653                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27654                         // by a directive with templateUrl when its template arrives.
27655                         block.clone = clone;
27656                         nextBlockMap[block.id] = block;
27657                         updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27658                       });
27659                     }
27660                   }
27661                   lastBlockMap = nextBlockMap;
27662                 });
27663               };
27664             }
27665           };
27666         }];
27667
27668         var NG_HIDE_CLASS = 'ng-hide';
27669         var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27670         /**
27671          * @ngdoc directive
27672          * @name ngShow
27673          * @multiElement
27674          *
27675          * @description
27676          * The `ngShow` directive shows or hides the given HTML element based on the expression
27677          * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27678          * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27679          * in AngularJS and sets the display style to none (using an !important flag).
27680          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27681          *
27682          * ```html
27683          * <!-- when $scope.myValue is truthy (element is visible) -->
27684          * <div ng-show="myValue"></div>
27685          *
27686          * <!-- when $scope.myValue is falsy (element is hidden) -->
27687          * <div ng-show="myValue" class="ng-hide"></div>
27688          * ```
27689          *
27690          * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27691          * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27692          * from the element causing the element not to appear hidden.
27693          *
27694          * ## Why is !important used?
27695          *
27696          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27697          * can be easily overridden by heavier selectors. For example, something as simple
27698          * as changing the display style on a HTML list item would make hidden elements appear visible.
27699          * This also becomes a bigger issue when dealing with CSS frameworks.
27700          *
27701          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27702          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27703          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27704          *
27705          * ### Overriding `.ng-hide`
27706          *
27707          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27708          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27709          * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27710          * with extra animation classes that can be added.
27711          *
27712          * ```css
27713          * .ng-hide:not(.ng-hide-animate) {
27714          *   /&#42; this is just another form of hiding an element &#42;/
27715          *   display: block!important;
27716          *   position: absolute;
27717          *   top: -9999px;
27718          *   left: -9999px;
27719          * }
27720          * ```
27721          *
27722          * By default you don't need to override in CSS anything and the animations will work around the display style.
27723          *
27724          * ## A note about animations with `ngShow`
27725          *
27726          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27727          * is true and false. This system works like the animation system present with ngClass except that
27728          * you must also include the !important flag to override the display property
27729          * so that you can perform an animation when the element is hidden during the time of the animation.
27730          *
27731          * ```css
27732          * //
27733          * //a working example can be found at the bottom of this page
27734          * //
27735          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27736          *   /&#42; this is required as of 1.3x to properly
27737          *      apply all styling in a show/hide animation &#42;/
27738          *   transition: 0s linear all;
27739          * }
27740          *
27741          * .my-element.ng-hide-add-active,
27742          * .my-element.ng-hide-remove-active {
27743          *   /&#42; the transition is defined in the active class &#42;/
27744          *   transition: 1s linear all;
27745          * }
27746          *
27747          * .my-element.ng-hide-add { ... }
27748          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27749          * .my-element.ng-hide-remove { ... }
27750          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27751          * ```
27752          *
27753          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27754          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27755          *
27756          * @animations
27757          * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27758          * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27759          *
27760          * @element ANY
27761          * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27762          *     then the element is shown or hidden respectively.
27763          *
27764          * @example
27765           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27766             <file name="index.html">
27767               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27768               <div>
27769                 Show:
27770                 <div class="check-element animate-show" ng-show="checked">
27771                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27772                 </div>
27773               </div>
27774               <div>
27775                 Hide:
27776                 <div class="check-element animate-show" ng-hide="checked">
27777                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27778                 </div>
27779               </div>
27780             </file>
27781             <file name="glyphicons.css">
27782               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27783             </file>
27784             <file name="animations.css">
27785               .animate-show {
27786                 line-height: 20px;
27787                 opacity: 1;
27788                 padding: 10px;
27789                 border: 1px solid black;
27790                 background: white;
27791               }
27792
27793               .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27794                 transition: all linear 0.5s;
27795               }
27796
27797               .animate-show.ng-hide {
27798                 line-height: 0;
27799                 opacity: 0;
27800                 padding: 0 10px;
27801               }
27802
27803               .check-element {
27804                 padding: 10px;
27805                 border: 1px solid black;
27806                 background: white;
27807               }
27808             </file>
27809             <file name="protractor.js" type="protractor">
27810               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27811               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27812
27813               it('should check ng-show / ng-hide', function() {
27814                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27815                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27816
27817                 element(by.model('checked')).click();
27818
27819                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27820                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27821               });
27822             </file>
27823           </example>
27824          */
27825         var ngShowDirective = ['$animate', function($animate) {
27826           return {
27827             restrict: 'A',
27828             multiElement: true,
27829             link: function(scope, element, attr) {
27830               scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27831                 // we're adding a temporary, animation-specific class for ng-hide since this way
27832                 // we can control when the element is actually displayed on screen without having
27833                 // to have a global/greedy CSS selector that breaks when other animations are run.
27834                 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27835                 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27836                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27837                 });
27838               });
27839             }
27840           };
27841         }];
27842
27843
27844         /**
27845          * @ngdoc directive
27846          * @name ngHide
27847          * @multiElement
27848          *
27849          * @description
27850          * The `ngHide` directive shows or hides the given HTML element based on the expression
27851          * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27852          * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27853          * in AngularJS and sets the display style to none (using an !important flag).
27854          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27855          *
27856          * ```html
27857          * <!-- when $scope.myValue is truthy (element is hidden) -->
27858          * <div ng-hide="myValue" class="ng-hide"></div>
27859          *
27860          * <!-- when $scope.myValue is falsy (element is visible) -->
27861          * <div ng-hide="myValue"></div>
27862          * ```
27863          *
27864          * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27865          * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27866          * from the element causing the element not to appear hidden.
27867          *
27868          * ## Why is !important used?
27869          *
27870          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27871          * can be easily overridden by heavier selectors. For example, something as simple
27872          * as changing the display style on a HTML list item would make hidden elements appear visible.
27873          * This also becomes a bigger issue when dealing with CSS frameworks.
27874          *
27875          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27876          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27877          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27878          *
27879          * ### Overriding `.ng-hide`
27880          *
27881          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27882          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27883          * class in CSS:
27884          *
27885          * ```css
27886          * .ng-hide {
27887          *   /&#42; this is just another form of hiding an element &#42;/
27888          *   display: block!important;
27889          *   position: absolute;
27890          *   top: -9999px;
27891          *   left: -9999px;
27892          * }
27893          * ```
27894          *
27895          * By default you don't need to override in CSS anything and the animations will work around the display style.
27896          *
27897          * ## A note about animations with `ngHide`
27898          *
27899          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27900          * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27901          * CSS class is added and removed for you instead of your own CSS class.
27902          *
27903          * ```css
27904          * //
27905          * //a working example can be found at the bottom of this page
27906          * //
27907          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27908          *   transition: 0.5s linear all;
27909          * }
27910          *
27911          * .my-element.ng-hide-add { ... }
27912          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27913          * .my-element.ng-hide-remove { ... }
27914          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27915          * ```
27916          *
27917          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27918          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27919          *
27920          * @animations
27921          * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27922          * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27923          *
27924          * @element ANY
27925          * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27926          *     the element is shown or hidden respectively.
27927          *
27928          * @example
27929           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27930             <file name="index.html">
27931               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27932               <div>
27933                 Show:
27934                 <div class="check-element animate-hide" ng-show="checked">
27935                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27936                 </div>
27937               </div>
27938               <div>
27939                 Hide:
27940                 <div class="check-element animate-hide" ng-hide="checked">
27941                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27942                 </div>
27943               </div>
27944             </file>
27945             <file name="glyphicons.css">
27946               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27947             </file>
27948             <file name="animations.css">
27949               .animate-hide {
27950                 transition: all linear 0.5s;
27951                 line-height: 20px;
27952                 opacity: 1;
27953                 padding: 10px;
27954                 border: 1px solid black;
27955                 background: white;
27956               }
27957
27958               .animate-hide.ng-hide {
27959                 line-height: 0;
27960                 opacity: 0;
27961                 padding: 0 10px;
27962               }
27963
27964               .check-element {
27965                 padding: 10px;
27966                 border: 1px solid black;
27967                 background: white;
27968               }
27969             </file>
27970             <file name="protractor.js" type="protractor">
27971               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27972               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27973
27974               it('should check ng-show / ng-hide', function() {
27975                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27976                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27977
27978                 element(by.model('checked')).click();
27979
27980                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27981                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27982               });
27983             </file>
27984           </example>
27985          */
27986         var ngHideDirective = ['$animate', function($animate) {
27987           return {
27988             restrict: 'A',
27989             multiElement: true,
27990             link: function(scope, element, attr) {
27991               scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27992                 // The comment inside of the ngShowDirective explains why we add and
27993                 // remove a temporary class for the show/hide animation
27994                 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27995                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27996                 });
27997               });
27998             }
27999           };
28000         }];
28001
28002         /**
28003          * @ngdoc directive
28004          * @name ngStyle
28005          * @restrict AC
28006          *
28007          * @description
28008          * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28009          *
28010          * @element ANY
28011          * @param {expression} ngStyle
28012          *
28013          * {@link guide/expression Expression} which evals to an
28014          * object whose keys are CSS style names and values are corresponding values for those CSS
28015          * keys.
28016          *
28017          * Since some CSS style names are not valid keys for an object, they must be quoted.
28018          * See the 'background-color' style in the example below.
28019          *
28020          * @example
28021            <example>
28022              <file name="index.html">
28023                 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28024                 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28025                 <input type="button" value="clear" ng-click="myStyle={}">
28026                 <br/>
28027                 <span ng-style="myStyle">Sample Text</span>
28028                 <pre>myStyle={{myStyle}}</pre>
28029              </file>
28030              <file name="style.css">
28031                span {
28032                  color: black;
28033                }
28034              </file>
28035              <file name="protractor.js" type="protractor">
28036                var colorSpan = element(by.css('span'));
28037
28038                it('should check ng-style', function() {
28039                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28040                  element(by.css('input[value=\'set color\']')).click();
28041                  expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28042                  element(by.css('input[value=clear]')).click();
28043                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28044                });
28045              </file>
28046            </example>
28047          */
28048         var ngStyleDirective = ngDirective(function(scope, element, attr) {
28049           scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28050             if (oldStyles && (newStyles !== oldStyles)) {
28051               forEach(oldStyles, function(val, style) { element.css(style, '');});
28052             }
28053             if (newStyles) element.css(newStyles);
28054           }, true);
28055         });
28056
28057         /**
28058          * @ngdoc directive
28059          * @name ngSwitch
28060          * @restrict EA
28061          *
28062          * @description
28063          * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28064          * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28065          * as specified in the template.
28066          *
28067          * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28068          * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28069          * matches the value obtained from the evaluated expression. In other words, you define a container element
28070          * (where you place the directive), place an expression on the **`on="..."` attribute**
28071          * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28072          * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28073          * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28074          * attribute is displayed.
28075          *
28076          * <div class="alert alert-info">
28077          * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28078          * as literal string values to match against.
28079          * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28080          * value of the expression `$scope.someVal`.
28081          * </div>
28082
28083          * @animations
28084          * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28085          * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28086          *
28087          * @usage
28088          *
28089          * ```
28090          * <ANY ng-switch="expression">
28091          *   <ANY ng-switch-when="matchValue1">...</ANY>
28092          *   <ANY ng-switch-when="matchValue2">...</ANY>
28093          *   <ANY ng-switch-default>...</ANY>
28094          * </ANY>
28095          * ```
28096          *
28097          *
28098          * @scope
28099          * @priority 1200
28100          * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28101          * On child elements add:
28102          *
28103          * * `ngSwitchWhen`: the case statement to match against. If match then this
28104          *   case will be displayed. If the same match appears multiple times, all the
28105          *   elements will be displayed.
28106          * * `ngSwitchDefault`: the default case when no other case match. If there
28107          *   are multiple default cases, all of them will be displayed when no other
28108          *   case match.
28109          *
28110          *
28111          * @example
28112           <example module="switchExample" deps="angular-animate.js" animations="true">
28113             <file name="index.html">
28114               <div ng-controller="ExampleController">
28115                 <select ng-model="selection" ng-options="item for item in items">
28116                 </select>
28117                 <code>selection={{selection}}</code>
28118                 <hr/>
28119                 <div class="animate-switch-container"
28120                   ng-switch on="selection">
28121                     <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28122                     <div class="animate-switch" ng-switch-when="home">Home Span</div>
28123                     <div class="animate-switch" ng-switch-default>default</div>
28124                 </div>
28125               </div>
28126             </file>
28127             <file name="script.js">
28128               angular.module('switchExample', ['ngAnimate'])
28129                 .controller('ExampleController', ['$scope', function($scope) {
28130                   $scope.items = ['settings', 'home', 'other'];
28131                   $scope.selection = $scope.items[0];
28132                 }]);
28133             </file>
28134             <file name="animations.css">
28135               .animate-switch-container {
28136                 position:relative;
28137                 background:white;
28138                 border:1px solid black;
28139                 height:40px;
28140                 overflow:hidden;
28141               }
28142
28143               .animate-switch {
28144                 padding:10px;
28145               }
28146
28147               .animate-switch.ng-animate {
28148                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28149
28150                 position:absolute;
28151                 top:0;
28152                 left:0;
28153                 right:0;
28154                 bottom:0;
28155               }
28156
28157               .animate-switch.ng-leave.ng-leave-active,
28158               .animate-switch.ng-enter {
28159                 top:-50px;
28160               }
28161               .animate-switch.ng-leave,
28162               .animate-switch.ng-enter.ng-enter-active {
28163                 top:0;
28164               }
28165             </file>
28166             <file name="protractor.js" type="protractor">
28167               var switchElem = element(by.css('[ng-switch]'));
28168               var select = element(by.model('selection'));
28169
28170               it('should start in settings', function() {
28171                 expect(switchElem.getText()).toMatch(/Settings Div/);
28172               });
28173               it('should change to home', function() {
28174                 select.all(by.css('option')).get(1).click();
28175                 expect(switchElem.getText()).toMatch(/Home Span/);
28176               });
28177               it('should select default', function() {
28178                 select.all(by.css('option')).get(2).click();
28179                 expect(switchElem.getText()).toMatch(/default/);
28180               });
28181             </file>
28182           </example>
28183          */
28184         var ngSwitchDirective = ['$animate', function($animate) {
28185           return {
28186             require: 'ngSwitch',
28187
28188             // asks for $scope to fool the BC controller module
28189             controller: ['$scope', function ngSwitchController() {
28190              this.cases = {};
28191             }],
28192             link: function(scope, element, attr, ngSwitchController) {
28193               var watchExpr = attr.ngSwitch || attr.on,
28194                   selectedTranscludes = [],
28195                   selectedElements = [],
28196                   previousLeaveAnimations = [],
28197                   selectedScopes = [];
28198
28199               var spliceFactory = function(array, index) {
28200                   return function() { array.splice(index, 1); };
28201               };
28202
28203               scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28204                 var i, ii;
28205                 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28206                   $animate.cancel(previousLeaveAnimations[i]);
28207                 }
28208                 previousLeaveAnimations.length = 0;
28209
28210                 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28211                   var selected = getBlockNodes(selectedElements[i].clone);
28212                   selectedScopes[i].$destroy();
28213                   var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28214                   promise.then(spliceFactory(previousLeaveAnimations, i));
28215                 }
28216
28217                 selectedElements.length = 0;
28218                 selectedScopes.length = 0;
28219
28220                 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28221                   forEach(selectedTranscludes, function(selectedTransclude) {
28222                     selectedTransclude.transclude(function(caseElement, selectedScope) {
28223                       selectedScopes.push(selectedScope);
28224                       var anchor = selectedTransclude.element;
28225                       caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28226                       var block = { clone: caseElement };
28227
28228                       selectedElements.push(block);
28229                       $animate.enter(caseElement, anchor.parent(), anchor);
28230                     });
28231                   });
28232                 }
28233               });
28234             }
28235           };
28236         }];
28237
28238         var ngSwitchWhenDirective = ngDirective({
28239           transclude: 'element',
28240           priority: 1200,
28241           require: '^ngSwitch',
28242           multiElement: true,
28243           link: function(scope, element, attrs, ctrl, $transclude) {
28244             ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28245             ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28246           }
28247         });
28248
28249         var ngSwitchDefaultDirective = ngDirective({
28250           transclude: 'element',
28251           priority: 1200,
28252           require: '^ngSwitch',
28253           multiElement: true,
28254           link: function(scope, element, attr, ctrl, $transclude) {
28255             ctrl.cases['?'] = (ctrl.cases['?'] || []);
28256             ctrl.cases['?'].push({ transclude: $transclude, element: element });
28257            }
28258         });
28259
28260         /**
28261          * @ngdoc directive
28262          * @name ngTransclude
28263          * @restrict EAC
28264          *
28265          * @description
28266          * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28267          *
28268          * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28269          *
28270          * @element ANY
28271          *
28272          * @example
28273            <example module="transcludeExample">
28274              <file name="index.html">
28275                <script>
28276                  angular.module('transcludeExample', [])
28277                   .directive('pane', function(){
28278                      return {
28279                        restrict: 'E',
28280                        transclude: true,
28281                        scope: { title:'@' },
28282                        template: '<div style="border: 1px solid black;">' +
28283                                    '<div style="background-color: gray">{{title}}</div>' +
28284                                    '<ng-transclude></ng-transclude>' +
28285                                  '</div>'
28286                      };
28287                  })
28288                  .controller('ExampleController', ['$scope', function($scope) {
28289                    $scope.title = 'Lorem Ipsum';
28290                    $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28291                  }]);
28292                </script>
28293                <div ng-controller="ExampleController">
28294                  <input ng-model="title" aria-label="title"> <br/>
28295                  <textarea ng-model="text" aria-label="text"></textarea> <br/>
28296                  <pane title="{{title}}">{{text}}</pane>
28297                </div>
28298              </file>
28299              <file name="protractor.js" type="protractor">
28300                 it('should have transcluded', function() {
28301                   var titleElement = element(by.model('title'));
28302                   titleElement.clear();
28303                   titleElement.sendKeys('TITLE');
28304                   var textElement = element(by.model('text'));
28305                   textElement.clear();
28306                   textElement.sendKeys('TEXT');
28307                   expect(element(by.binding('title')).getText()).toEqual('TITLE');
28308                   expect(element(by.binding('text')).getText()).toEqual('TEXT');
28309                 });
28310              </file>
28311            </example>
28312          *
28313          */
28314         var ngTranscludeDirective = ngDirective({
28315           restrict: 'EAC',
28316           link: function($scope, $element, $attrs, controller, $transclude) {
28317             if (!$transclude) {
28318               throw minErr('ngTransclude')('orphan',
28319                'Illegal use of ngTransclude directive in the template! ' +
28320                'No parent directive that requires a transclusion found. ' +
28321                'Element: {0}',
28322                startingTag($element));
28323             }
28324
28325             $transclude(function(clone) {
28326               $element.empty();
28327               $element.append(clone);
28328             });
28329           }
28330         });
28331
28332         /**
28333          * @ngdoc directive
28334          * @name script
28335          * @restrict E
28336          *
28337          * @description
28338          * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28339          * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28340          * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28341          * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28342          * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28343          *
28344          * @param {string} type Must be set to `'text/ng-template'`.
28345          * @param {string} id Cache name of the template.
28346          *
28347          * @example
28348           <example>
28349             <file name="index.html">
28350               <script type="text/ng-template" id="/tpl.html">
28351                 Content of the template.
28352               </script>
28353
28354               <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28355               <div id="tpl-content" ng-include src="currentTpl"></div>
28356             </file>
28357             <file name="protractor.js" type="protractor">
28358               it('should load template defined inside script tag', function() {
28359                 element(by.css('#tpl-link')).click();
28360                 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28361               });
28362             </file>
28363           </example>
28364          */
28365         var scriptDirective = ['$templateCache', function($templateCache) {
28366           return {
28367             restrict: 'E',
28368             terminal: true,
28369             compile: function(element, attr) {
28370               if (attr.type == 'text/ng-template') {
28371                 var templateUrl = attr.id,
28372                     text = element[0].text;
28373
28374                 $templateCache.put(templateUrl, text);
28375               }
28376             }
28377           };
28378         }];
28379
28380         var noopNgModelController = { $setViewValue: noop, $render: noop };
28381
28382         function chromeHack(optionElement) {
28383           // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28384           // Adding an <option selected="selected"> element to a <select required="required"> should
28385           // automatically select the new element
28386           if (optionElement[0].hasAttribute('selected')) {
28387             optionElement[0].selected = true;
28388           }
28389         }
28390
28391         /**
28392          * @ngdoc type
28393          * @name  select.SelectController
28394          * @description
28395          * The controller for the `<select>` directive. This provides support for reading
28396          * and writing the selected value(s) of the control and also coordinates dynamically
28397          * added `<option>` elements, perhaps by an `ngRepeat` directive.
28398          */
28399         var SelectController =
28400                 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28401
28402           var self = this,
28403               optionsMap = new HashMap();
28404
28405           // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28406           self.ngModelCtrl = noopNgModelController;
28407
28408           // The "unknown" option is one that is prepended to the list if the viewValue
28409           // does not match any of the options. When it is rendered the value of the unknown
28410           // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28411           //
28412           // We can't just jqLite('<option>') since jqLite is not smart enough
28413           // to create it in <select> and IE barfs otherwise.
28414           self.unknownOption = jqLite(document.createElement('option'));
28415           self.renderUnknownOption = function(val) {
28416             var unknownVal = '? ' + hashKey(val) + ' ?';
28417             self.unknownOption.val(unknownVal);
28418             $element.prepend(self.unknownOption);
28419             $element.val(unknownVal);
28420           };
28421
28422           $scope.$on('$destroy', function() {
28423             // disable unknown option so that we don't do work when the whole select is being destroyed
28424             self.renderUnknownOption = noop;
28425           });
28426
28427           self.removeUnknownOption = function() {
28428             if (self.unknownOption.parent()) self.unknownOption.remove();
28429           };
28430
28431
28432           // Read the value of the select control, the implementation of this changes depending
28433           // upon whether the select can have multiple values and whether ngOptions is at work.
28434           self.readValue = function readSingleValue() {
28435             self.removeUnknownOption();
28436             return $element.val();
28437           };
28438
28439
28440           // Write the value to the select control, the implementation of this changes depending
28441           // upon whether the select can have multiple values and whether ngOptions is at work.
28442           self.writeValue = function writeSingleValue(value) {
28443             if (self.hasOption(value)) {
28444               self.removeUnknownOption();
28445               $element.val(value);
28446               if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28447             } else {
28448               if (value == null && self.emptyOption) {
28449                 self.removeUnknownOption();
28450                 $element.val('');
28451               } else {
28452                 self.renderUnknownOption(value);
28453               }
28454             }
28455           };
28456
28457
28458           // Tell the select control that an option, with the given value, has been added
28459           self.addOption = function(value, element) {
28460             assertNotHasOwnProperty(value, '"option value"');
28461             if (value === '') {
28462               self.emptyOption = element;
28463             }
28464             var count = optionsMap.get(value) || 0;
28465             optionsMap.put(value, count + 1);
28466             self.ngModelCtrl.$render();
28467             chromeHack(element);
28468           };
28469
28470           // Tell the select control that an option, with the given value, has been removed
28471           self.removeOption = function(value) {
28472             var count = optionsMap.get(value);
28473             if (count) {
28474               if (count === 1) {
28475                 optionsMap.remove(value);
28476                 if (value === '') {
28477                   self.emptyOption = undefined;
28478                 }
28479               } else {
28480                 optionsMap.put(value, count - 1);
28481               }
28482             }
28483           };
28484
28485           // Check whether the select control has an option matching the given value
28486           self.hasOption = function(value) {
28487             return !!optionsMap.get(value);
28488           };
28489
28490
28491           self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28492
28493             if (interpolateValueFn) {
28494               // The value attribute is interpolated
28495               var oldVal;
28496               optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28497                 if (isDefined(oldVal)) {
28498                   self.removeOption(oldVal);
28499                 }
28500                 oldVal = newVal;
28501                 self.addOption(newVal, optionElement);
28502               });
28503             } else if (interpolateTextFn) {
28504               // The text content is interpolated
28505               optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28506                 optionAttrs.$set('value', newVal);
28507                 if (oldVal !== newVal) {
28508                   self.removeOption(oldVal);
28509                 }
28510                 self.addOption(newVal, optionElement);
28511               });
28512             } else {
28513               // The value attribute is static
28514               self.addOption(optionAttrs.value, optionElement);
28515             }
28516
28517             optionElement.on('$destroy', function() {
28518               self.removeOption(optionAttrs.value);
28519               self.ngModelCtrl.$render();
28520             });
28521           };
28522         }];
28523
28524         /**
28525          * @ngdoc directive
28526          * @name select
28527          * @restrict E
28528          *
28529          * @description
28530          * HTML `SELECT` element with angular data-binding.
28531          *
28532          * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28533          * between the scope and the `<select>` control (including setting default values).
28534          * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28535          * {@link ngOptions `ngOptions`} directives.
28536          *
28537          * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28538          * to the model identified by the `ngModel` directive. With static or repeated options, this is
28539          * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28540          * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28541          *
28542          * <div class="alert alert-warning">
28543          * Note that the value of a `select` directive used without `ngOptions` is always a string.
28544          * When the model needs to be bound to a non-string value, you must either explictly convert it
28545          * using a directive (see example below) or use `ngOptions` to specify the set of options.
28546          * This is because an option element can only be bound to string values at present.
28547          * </div>
28548          *
28549          * If the viewValue of `ngModel` does not match any of the options, then the control
28550          * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28551          *
28552          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28553          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28554          * option. See example below for demonstration.
28555          *
28556          * <div class="alert alert-info">
28557          * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28558          * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28559          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28560          * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28561          * a new scope for each repeated instance.
28562          * </div>
28563          *
28564          *
28565          * @param {string} ngModel Assignable angular expression to data-bind to.
28566          * @param {string=} name Property name of the form under which the control is published.
28567          * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28568          *     bound to the model as an array.
28569          * @param {string=} required Sets `required` validation error key if the value is not entered.
28570          * @param {string=} ngRequired Adds required attribute and required validation constraint to
28571          * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28572          * when you want to data-bind to the required attribute.
28573          * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28574          *    interaction with the select element.
28575          * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28576          * set on the model on selection. See {@link ngOptions `ngOptions`}.
28577          *
28578          * @example
28579          * ### Simple `select` elements with static options
28580          *
28581          * <example name="static-select" module="staticSelect">
28582          * <file name="index.html">
28583          * <div ng-controller="ExampleController">
28584          *   <form name="myForm">
28585          *     <label for="singleSelect"> Single select: </label><br>
28586          *     <select name="singleSelect" ng-model="data.singleSelect">
28587          *       <option value="option-1">Option 1</option>
28588          *       <option value="option-2">Option 2</option>
28589          *     </select><br>
28590          *
28591          *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28592          *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28593          *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28594          *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28595          *       <option value="option-2">Option 2</option>
28596          *     </select><br>
28597          *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28598          *     <tt>singleSelect = {{data.singleSelect}}</tt>
28599          *
28600          *     <hr>
28601          *     <label for="multipleSelect"> Multiple select: </label><br>
28602          *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28603          *       <option value="option-1">Option 1</option>
28604          *       <option value="option-2">Option 2</option>
28605          *       <option value="option-3">Option 3</option>
28606          *     </select><br>
28607          *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28608          *   </form>
28609          * </div>
28610          * </file>
28611          * <file name="app.js">
28612          *  angular.module('staticSelect', [])
28613          *    .controller('ExampleController', ['$scope', function($scope) {
28614          *      $scope.data = {
28615          *       singleSelect: null,
28616          *       multipleSelect: [],
28617          *       option1: 'option-1',
28618          *      };
28619          *
28620          *      $scope.forceUnknownOption = function() {
28621          *        $scope.data.singleSelect = 'nonsense';
28622          *      };
28623          *   }]);
28624          * </file>
28625          *</example>
28626          *
28627          * ### Using `ngRepeat` to generate `select` options
28628          * <example name="ngrepeat-select" module="ngrepeatSelect">
28629          * <file name="index.html">
28630          * <div ng-controller="ExampleController">
28631          *   <form name="myForm">
28632          *     <label for="repeatSelect"> Repeat select: </label>
28633          *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28634          *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28635          *     </select>
28636          *   </form>
28637          *   <hr>
28638          *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28639          * </div>
28640          * </file>
28641          * <file name="app.js">
28642          *  angular.module('ngrepeatSelect', [])
28643          *    .controller('ExampleController', ['$scope', function($scope) {
28644          *      $scope.data = {
28645          *       repeatSelect: null,
28646          *       availableOptions: [
28647          *         {id: '1', name: 'Option A'},
28648          *         {id: '2', name: 'Option B'},
28649          *         {id: '3', name: 'Option C'}
28650          *       ],
28651          *      };
28652          *   }]);
28653          * </file>
28654          *</example>
28655          *
28656          *
28657          * ### Using `select` with `ngOptions` and setting a default value
28658          * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28659          *
28660          * <example name="select-with-default-values" module="defaultValueSelect">
28661          * <file name="index.html">
28662          * <div ng-controller="ExampleController">
28663          *   <form name="myForm">
28664          *     <label for="mySelect">Make a choice:</label>
28665          *     <select name="mySelect" id="mySelect"
28666          *       ng-options="option.name for option in data.availableOptions track by option.id"
28667          *       ng-model="data.selectedOption"></select>
28668          *   </form>
28669          *   <hr>
28670          *   <tt>option = {{data.selectedOption}}</tt><br/>
28671          * </div>
28672          * </file>
28673          * <file name="app.js">
28674          *  angular.module('defaultValueSelect', [])
28675          *    .controller('ExampleController', ['$scope', function($scope) {
28676          *      $scope.data = {
28677          *       availableOptions: [
28678          *         {id: '1', name: 'Option A'},
28679          *         {id: '2', name: 'Option B'},
28680          *         {id: '3', name: 'Option C'}
28681          *       ],
28682          *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28683          *       };
28684          *   }]);
28685          * </file>
28686          *</example>
28687          *
28688          *
28689          * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28690          *
28691          * <example name="select-with-non-string-options" module="nonStringSelect">
28692          *   <file name="index.html">
28693          *     <select ng-model="model.id" convert-to-number>
28694          *       <option value="0">Zero</option>
28695          *       <option value="1">One</option>
28696          *       <option value="2">Two</option>
28697          *     </select>
28698          *     {{ model }}
28699          *   </file>
28700          *   <file name="app.js">
28701          *     angular.module('nonStringSelect', [])
28702          *       .run(function($rootScope) {
28703          *         $rootScope.model = { id: 2 };
28704          *       })
28705          *       .directive('convertToNumber', function() {
28706          *         return {
28707          *           require: 'ngModel',
28708          *           link: function(scope, element, attrs, ngModel) {
28709          *             ngModel.$parsers.push(function(val) {
28710          *               return parseInt(val, 10);
28711          *             });
28712          *             ngModel.$formatters.push(function(val) {
28713          *               return '' + val;
28714          *             });
28715          *           }
28716          *         };
28717          *       });
28718          *   </file>
28719          *   <file name="protractor.js" type="protractor">
28720          *     it('should initialize to model', function() {
28721          *       var select = element(by.css('select'));
28722          *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28723          *     });
28724          *   </file>
28725          * </example>
28726          *
28727          */
28728         var selectDirective = function() {
28729
28730           return {
28731             restrict: 'E',
28732             require: ['select', '?ngModel'],
28733             controller: SelectController,
28734             priority: 1,
28735             link: {
28736               pre: selectPreLink
28737             }
28738           };
28739
28740           function selectPreLink(scope, element, attr, ctrls) {
28741
28742               // if ngModel is not defined, we don't need to do anything
28743               var ngModelCtrl = ctrls[1];
28744               if (!ngModelCtrl) return;
28745
28746               var selectCtrl = ctrls[0];
28747
28748               selectCtrl.ngModelCtrl = ngModelCtrl;
28749
28750               // We delegate rendering to the `writeValue` method, which can be changed
28751               // if the select can have multiple selected values or if the options are being
28752               // generated by `ngOptions`
28753               ngModelCtrl.$render = function() {
28754                 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28755               };
28756
28757               // When the selected item(s) changes we delegate getting the value of the select control
28758               // to the `readValue` method, which can be changed if the select can have multiple
28759               // selected values or if the options are being generated by `ngOptions`
28760               element.on('change', function() {
28761                 scope.$apply(function() {
28762                   ngModelCtrl.$setViewValue(selectCtrl.readValue());
28763                 });
28764               });
28765
28766               // If the select allows multiple values then we need to modify how we read and write
28767               // values from and to the control; also what it means for the value to be empty and
28768               // we have to add an extra watch since ngModel doesn't work well with arrays - it
28769               // doesn't trigger rendering if only an item in the array changes.
28770               if (attr.multiple) {
28771
28772                 // Read value now needs to check each option to see if it is selected
28773                 selectCtrl.readValue = function readMultipleValue() {
28774                   var array = [];
28775                   forEach(element.find('option'), function(option) {
28776                     if (option.selected) {
28777                       array.push(option.value);
28778                     }
28779                   });
28780                   return array;
28781                 };
28782
28783                 // Write value now needs to set the selected property of each matching option
28784                 selectCtrl.writeValue = function writeMultipleValue(value) {
28785                   var items = new HashMap(value);
28786                   forEach(element.find('option'), function(option) {
28787                     option.selected = isDefined(items.get(option.value));
28788                   });
28789                 };
28790
28791                 // we have to do it on each watch since ngModel watches reference, but
28792                 // we need to work of an array, so we need to see if anything was inserted/removed
28793                 var lastView, lastViewRef = NaN;
28794                 scope.$watch(function selectMultipleWatch() {
28795                   if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28796                     lastView = shallowCopy(ngModelCtrl.$viewValue);
28797                     ngModelCtrl.$render();
28798                   }
28799                   lastViewRef = ngModelCtrl.$viewValue;
28800                 });
28801
28802                 // If we are a multiple select then value is now a collection
28803                 // so the meaning of $isEmpty changes
28804                 ngModelCtrl.$isEmpty = function(value) {
28805                   return !value || value.length === 0;
28806                 };
28807
28808               }
28809             }
28810         };
28811
28812
28813         // The option directive is purely designed to communicate the existence (or lack of)
28814         // of dynamically created (and destroyed) option elements to their containing select
28815         // directive via its controller.
28816         var optionDirective = ['$interpolate', function($interpolate) {
28817           return {
28818             restrict: 'E',
28819             priority: 100,
28820             compile: function(element, attr) {
28821
28822               if (isDefined(attr.value)) {
28823                 // If the value attribute is defined, check if it contains an interpolation
28824                 var interpolateValueFn = $interpolate(attr.value, true);
28825               } else {
28826                 // If the value attribute is not defined then we fall back to the
28827                 // text content of the option element, which may be interpolated
28828                 var interpolateTextFn = $interpolate(element.text(), true);
28829                 if (!interpolateTextFn) {
28830                   attr.$set('value', element.text());
28831                 }
28832               }
28833
28834               return function(scope, element, attr) {
28835
28836                 // This is an optimization over using ^^ since we don't want to have to search
28837                 // all the way to the root of the DOM for every single option element
28838                 var selectCtrlName = '$selectController',
28839                     parent = element.parent(),
28840                     selectCtrl = parent.data(selectCtrlName) ||
28841                       parent.parent().data(selectCtrlName); // in case we are in optgroup
28842
28843                 if (selectCtrl) {
28844                   selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28845                 }
28846               };
28847             }
28848           };
28849         }];
28850
28851         var styleDirective = valueFn({
28852           restrict: 'E',
28853           terminal: false
28854         });
28855
28856         var requiredDirective = function() {
28857           return {
28858             restrict: 'A',
28859             require: '?ngModel',
28860             link: function(scope, elm, attr, ctrl) {
28861               if (!ctrl) return;
28862               attr.required = true; // force truthy in case we are on non input element
28863
28864               ctrl.$validators.required = function(modelValue, viewValue) {
28865                 return !attr.required || !ctrl.$isEmpty(viewValue);
28866               };
28867
28868               attr.$observe('required', function() {
28869                 ctrl.$validate();
28870               });
28871             }
28872           };
28873         };
28874
28875
28876         var patternDirective = function() {
28877           return {
28878             restrict: 'A',
28879             require: '?ngModel',
28880             link: function(scope, elm, attr, ctrl) {
28881               if (!ctrl) return;
28882
28883               var regexp, patternExp = attr.ngPattern || attr.pattern;
28884               attr.$observe('pattern', function(regex) {
28885                 if (isString(regex) && regex.length > 0) {
28886                   regex = new RegExp('^' + regex + '$');
28887                 }
28888
28889                 if (regex && !regex.test) {
28890                   throw minErr('ngPattern')('noregexp',
28891                     'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28892                     regex, startingTag(elm));
28893                 }
28894
28895                 regexp = regex || undefined;
28896                 ctrl.$validate();
28897               });
28898
28899               ctrl.$validators.pattern = function(modelValue, viewValue) {
28900                 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28901                 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28902               };
28903             }
28904           };
28905         };
28906
28907
28908         var maxlengthDirective = function() {
28909           return {
28910             restrict: 'A',
28911             require: '?ngModel',
28912             link: function(scope, elm, attr, ctrl) {
28913               if (!ctrl) return;
28914
28915               var maxlength = -1;
28916               attr.$observe('maxlength', function(value) {
28917                 var intVal = toInt(value);
28918                 maxlength = isNaN(intVal) ? -1 : intVal;
28919                 ctrl.$validate();
28920               });
28921               ctrl.$validators.maxlength = function(modelValue, viewValue) {
28922                 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28923               };
28924             }
28925           };
28926         };
28927
28928         var minlengthDirective = function() {
28929           return {
28930             restrict: 'A',
28931             require: '?ngModel',
28932             link: function(scope, elm, attr, ctrl) {
28933               if (!ctrl) return;
28934
28935               var minlength = 0;
28936               attr.$observe('minlength', function(value) {
28937                 minlength = toInt(value) || 0;
28938                 ctrl.$validate();
28939               });
28940               ctrl.$validators.minlength = function(modelValue, viewValue) {
28941                 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28942               };
28943             }
28944           };
28945         };
28946
28947         if (window.angular.bootstrap) {
28948           //AngularJS is already loaded, so we can return here...
28949           console.log('WARNING: Tried to load angular more than once.');
28950           return;
28951         }
28952
28953         //try to bind to jquery now so that one can write jqLite(document).ready()
28954         //but we will rebind on bootstrap again.
28955         bindJQuery();
28956
28957         publishExternalAPI(angular);
28958
28959         angular.module("ngLocale", [], ["$provide", function($provide) {
28960         var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28961         function getDecimals(n) {
28962           n = n + '';
28963           var i = n.indexOf('.');
28964           return (i == -1) ? 0 : n.length - i - 1;
28965         }
28966
28967         function getVF(n, opt_precision) {
28968           var v = opt_precision;
28969
28970           if (undefined === v) {
28971             v = Math.min(getDecimals(n), 3);
28972           }
28973
28974           var base = Math.pow(10, v);
28975           var f = ((n * base) | 0) % base;
28976           return {v: v, f: f};
28977         }
28978
28979         $provide.value("$locale", {
28980           "DATETIME_FORMATS": {
28981             "AMPMS": [
28982               "AM",
28983               "PM"
28984             ],
28985             "DAY": [
28986               "Sunday",
28987               "Monday",
28988               "Tuesday",
28989               "Wednesday",
28990               "Thursday",
28991               "Friday",
28992               "Saturday"
28993             ],
28994             "ERANAMES": [
28995               "Before Christ",
28996               "Anno Domini"
28997             ],
28998             "ERAS": [
28999               "BC",
29000               "AD"
29001             ],
29002             "FIRSTDAYOFWEEK": 6,
29003             "MONTH": [
29004               "January",
29005               "February",
29006               "March",
29007               "April",
29008               "May",
29009               "June",
29010               "July",
29011               "August",
29012               "September",
29013               "October",
29014               "November",
29015               "December"
29016             ],
29017             "SHORTDAY": [
29018               "Sun",
29019               "Mon",
29020               "Tue",
29021               "Wed",
29022               "Thu",
29023               "Fri",
29024               "Sat"
29025             ],
29026             "SHORTMONTH": [
29027               "Jan",
29028               "Feb",
29029               "Mar",
29030               "Apr",
29031               "May",
29032               "Jun",
29033               "Jul",
29034               "Aug",
29035               "Sep",
29036               "Oct",
29037               "Nov",
29038               "Dec"
29039             ],
29040             "WEEKENDRANGE": [
29041               5,
29042               6
29043             ],
29044             "fullDate": "EEEE, MMMM d, y",
29045             "longDate": "MMMM d, y",
29046             "medium": "MMM d, y h:mm:ss a",
29047             "mediumDate": "MMM d, y",
29048             "mediumTime": "h:mm:ss a",
29049             "short": "M/d/yy h:mm a",
29050             "shortDate": "M/d/yy",
29051             "shortTime": "h:mm a"
29052           },
29053           "NUMBER_FORMATS": {
29054             "CURRENCY_SYM": "$",
29055             "DECIMAL_SEP": ".",
29056             "GROUP_SEP": ",",
29057             "PATTERNS": [
29058               {
29059                 "gSize": 3,
29060                 "lgSize": 3,
29061                 "maxFrac": 3,
29062                 "minFrac": 0,
29063                 "minInt": 1,
29064                 "negPre": "-",
29065                 "negSuf": "",
29066                 "posPre": "",
29067                 "posSuf": ""
29068               },
29069               {
29070                 "gSize": 3,
29071                 "lgSize": 3,
29072                 "maxFrac": 2,
29073                 "minFrac": 2,
29074                 "minInt": 1,
29075                 "negPre": "-\u00a4",
29076                 "negSuf": "",
29077                 "posPre": "\u00a4",
29078                 "posSuf": ""
29079               }
29080             ]
29081           },
29082           "id": "en-us",
29083           "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;}
29084         });
29085         }]);
29086
29087           jqLite(document).ready(function() {
29088             angularInit(document, bootstrap);
29089           });
29090
29091         })(window, document);
29092
29093         !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>');
29094
29095 /***/ },
29096 /* 3 */
29097 /***/ function(module, exports) {
29098
29099         /**
29100          * State-based routing for AngularJS
29101          * @version v0.2.15
29102          * @link http://angular-ui.github.com/
29103          * @license MIT License, http://www.opensource.org/licenses/MIT
29104          */
29105
29106         /* commonjs package manager support (eg componentjs) */
29107         if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
29108           module.exports = 'ui.router';
29109         }
29110
29111         (function (window, angular, undefined) {
29112         /*jshint globalstrict:true*/
29113         /*global angular:false*/
29114         'use strict';
29115
29116         var isDefined = angular.isDefined,
29117             isFunction = angular.isFunction,
29118             isString = angular.isString,
29119             isObject = angular.isObject,
29120             isArray = angular.isArray,
29121             forEach = angular.forEach,
29122             extend = angular.extend,
29123             copy = angular.copy;
29124
29125         function inherit(parent, extra) {
29126           return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29127         }
29128
29129         function merge(dst) {
29130           forEach(arguments, function(obj) {
29131             if (obj !== dst) {
29132               forEach(obj, function(value, key) {
29133                 if (!dst.hasOwnProperty(key)) dst[key] = value;
29134               });
29135             }
29136           });
29137           return dst;
29138         }
29139
29140         /**
29141          * Finds the common ancestor path between two states.
29142          *
29143          * @param {Object} first The first state.
29144          * @param {Object} second The second state.
29145          * @return {Array} Returns an array of state names in descending order, not including the root.
29146          */
29147         function ancestors(first, second) {
29148           var path = [];
29149
29150           for (var n in first.path) {
29151             if (first.path[n] !== second.path[n]) break;
29152             path.push(first.path[n]);
29153           }
29154           return path;
29155         }
29156
29157         /**
29158          * IE8-safe wrapper for `Object.keys()`.
29159          *
29160          * @param {Object} object A JavaScript object.
29161          * @return {Array} Returns the keys of the object as an array.
29162          */
29163         function objectKeys(object) {
29164           if (Object.keys) {
29165             return Object.keys(object);
29166           }
29167           var result = [];
29168
29169           forEach(object, function(val, key) {
29170             result.push(key);
29171           });
29172           return result;
29173         }
29174
29175         /**
29176          * IE8-safe wrapper for `Array.prototype.indexOf()`.
29177          *
29178          * @param {Array} array A JavaScript array.
29179          * @param {*} value A value to search the array for.
29180          * @return {Number} Returns the array index value of `value`, or `-1` if not present.
29181          */
29182         function indexOf(array, value) {
29183           if (Array.prototype.indexOf) {
29184             return array.indexOf(value, Number(arguments[2]) || 0);
29185           }
29186           var len = array.length >>> 0, from = Number(arguments[2]) || 0;
29187           from = (from < 0) ? Math.ceil(from) : Math.floor(from);
29188
29189           if (from < 0) from += len;
29190
29191           for (; from < len; from++) {
29192             if (from in array && array[from] === value) return from;
29193           }
29194           return -1;
29195         }
29196
29197         /**
29198          * Merges a set of parameters with all parameters inherited between the common parents of the
29199          * current state and a given destination state.
29200          *
29201          * @param {Object} currentParams The value of the current state parameters ($stateParams).
29202          * @param {Object} newParams The set of parameters which will be composited with inherited params.
29203          * @param {Object} $current Internal definition of object representing the current state.
29204          * @param {Object} $to Internal definition of object representing state to transition to.
29205          */
29206         function inheritParams(currentParams, newParams, $current, $to) {
29207           var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
29208
29209           for (var i in parents) {
29210             if (!parents[i].params) continue;
29211             parentParams = objectKeys(parents[i].params);
29212             if (!parentParams.length) continue;
29213
29214             for (var j in parentParams) {
29215               if (indexOf(inheritList, parentParams[j]) >= 0) continue;
29216               inheritList.push(parentParams[j]);
29217               inherited[parentParams[j]] = currentParams[parentParams[j]];
29218             }
29219           }
29220           return extend({}, inherited, newParams);
29221         }
29222
29223         /**
29224          * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
29225          *
29226          * @param {Object} a The first object.
29227          * @param {Object} b The second object.
29228          * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
29229          *                     it defaults to the list of keys in `a`.
29230          * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
29231          */
29232         function equalForKeys(a, b, keys) {
29233           if (!keys) {
29234             keys = [];
29235             for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
29236           }
29237
29238           for (var i=0; i<keys.length; i++) {
29239             var k = keys[i];
29240             if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
29241           }
29242           return true;
29243         }
29244
29245         /**
29246          * Returns the subset of an object, based on a list of keys.
29247          *
29248          * @param {Array} keys
29249          * @param {Object} values
29250          * @return {Boolean} Returns a subset of `values`.
29251          */
29252         function filterByKeys(keys, values) {
29253           var filtered = {};
29254
29255           forEach(keys, function (name) {
29256             filtered[name] = values[name];
29257           });
29258           return filtered;
29259         }
29260
29261         // like _.indexBy
29262         // when you know that your index values will be unique, or you want last-one-in to win
29263         function indexBy(array, propName) {
29264           var result = {};
29265           forEach(array, function(item) {
29266             result[item[propName]] = item;
29267           });
29268           return result;
29269         }
29270
29271         // extracted from underscore.js
29272         // Return a copy of the object only containing the whitelisted properties.
29273         function pick(obj) {
29274           var copy = {};
29275           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29276           forEach(keys, function(key) {
29277             if (key in obj) copy[key] = obj[key];
29278           });
29279           return copy;
29280         }
29281
29282         // extracted from underscore.js
29283         // Return a copy of the object omitting the blacklisted properties.
29284         function omit(obj) {
29285           var copy = {};
29286           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29287           for (var key in obj) {
29288             if (indexOf(keys, key) == -1) copy[key] = obj[key];
29289           }
29290           return copy;
29291         }
29292
29293         function pluck(collection, key) {
29294           var result = isArray(collection) ? [] : {};
29295
29296           forEach(collection, function(val, i) {
29297             result[i] = isFunction(key) ? key(val) : val[key];
29298           });
29299           return result;
29300         }
29301
29302         function filter(collection, callback) {
29303           var array = isArray(collection);
29304           var result = array ? [] : {};
29305           forEach(collection, function(val, i) {
29306             if (callback(val, i)) {
29307               result[array ? result.length : i] = val;
29308             }
29309           });
29310           return result;
29311         }
29312
29313         function map(collection, callback) {
29314           var result = isArray(collection) ? [] : {};
29315
29316           forEach(collection, function(val, i) {
29317             result[i] = callback(val, i);
29318           });
29319           return result;
29320         }
29321
29322         /**
29323          * @ngdoc overview
29324          * @name ui.router.util
29325          *
29326          * @description
29327          * # ui.router.util sub-module
29328          *
29329          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29330          * in your angular app (use {@link ui.router} module instead).
29331          *
29332          */
29333         angular.module('ui.router.util', ['ng']);
29334
29335         /**
29336          * @ngdoc overview
29337          * @name ui.router.router
29338          * 
29339          * @requires ui.router.util
29340          *
29341          * @description
29342          * # ui.router.router sub-module
29343          *
29344          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29345          * in your angular app (use {@link ui.router} module instead).
29346          */
29347         angular.module('ui.router.router', ['ui.router.util']);
29348
29349         /**
29350          * @ngdoc overview
29351          * @name ui.router.state
29352          * 
29353          * @requires ui.router.router
29354          * @requires ui.router.util
29355          *
29356          * @description
29357          * # ui.router.state sub-module
29358          *
29359          * This module is a dependency of the main ui.router module. Do not include this module as a dependency
29360          * in your angular app (use {@link ui.router} module instead).
29361          * 
29362          */
29363         angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
29364
29365         /**
29366          * @ngdoc overview
29367          * @name ui.router
29368          *
29369          * @requires ui.router.state
29370          *
29371          * @description
29372          * # ui.router
29373          * 
29374          * ## The main module for ui.router 
29375          * There are several sub-modules included with the ui.router module, however only this module is needed
29376          * as a dependency within your angular app. The other modules are for organization purposes. 
29377          *
29378          * The modules are:
29379          * * ui.router - the main "umbrella" module
29380          * * ui.router.router - 
29381          * 
29382          * *You'll need to include **only** this module as the dependency within your angular app.*
29383          * 
29384          * <pre>
29385          * <!doctype html>
29386          * <html ng-app="myApp">
29387          * <head>
29388          *   <script src="js/angular.js"></script>
29389          *   <!-- Include the ui-router script -->
29390          *   <script src="js/angular-ui-router.min.js"></script>
29391          *   <script>
29392          *     // ...and add 'ui.router' as a dependency
29393          *     var myApp = angular.module('myApp', ['ui.router']);
29394          *   </script>
29395          * </head>
29396          * <body>
29397          * </body>
29398          * </html>
29399          * </pre>
29400          */
29401         angular.module('ui.router', ['ui.router.state']);
29402
29403         angular.module('ui.router.compat', ['ui.router']);
29404
29405         /**
29406          * @ngdoc object
29407          * @name ui.router.util.$resolve
29408          *
29409          * @requires $q
29410          * @requires $injector
29411          *
29412          * @description
29413          * Manages resolution of (acyclic) graphs of promises.
29414          */
29415         $Resolve.$inject = ['$q', '$injector'];
29416         function $Resolve(  $q,    $injector) {
29417           
29418           var VISIT_IN_PROGRESS = 1,
29419               VISIT_DONE = 2,
29420               NOTHING = {},
29421               NO_DEPENDENCIES = [],
29422               NO_LOCALS = NOTHING,
29423               NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
29424           
29425
29426           /**
29427            * @ngdoc function
29428            * @name ui.router.util.$resolve#study
29429            * @methodOf ui.router.util.$resolve
29430            *
29431            * @description
29432            * Studies a set of invocables that are likely to be used multiple times.
29433            * <pre>
29434            * $resolve.study(invocables)(locals, parent, self)
29435            * </pre>
29436            * is equivalent to
29437            * <pre>
29438            * $resolve.resolve(invocables, locals, parent, self)
29439            * </pre>
29440            * but the former is more efficient (in fact `resolve` just calls `study` 
29441            * internally).
29442            *
29443            * @param {object} invocables Invocable objects
29444            * @return {function} a function to pass in locals, parent and self
29445            */
29446           this.study = function (invocables) {
29447             if (!isObject(invocables)) throw new Error("'invocables' must be an object");
29448             var invocableKeys = objectKeys(invocables || {});
29449             
29450             // Perform a topological sort of invocables to build an ordered plan
29451             var plan = [], cycle = [], visited = {};
29452             function visit(value, key) {
29453               if (visited[key] === VISIT_DONE) return;
29454               
29455               cycle.push(key);
29456               if (visited[key] === VISIT_IN_PROGRESS) {
29457                 cycle.splice(0, indexOf(cycle, key));
29458                 throw new Error("Cyclic dependency: " + cycle.join(" -> "));
29459               }
29460               visited[key] = VISIT_IN_PROGRESS;
29461               
29462               if (isString(value)) {
29463                 plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
29464               } else {
29465                 var params = $injector.annotate(value);
29466                 forEach(params, function (param) {
29467                   if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
29468                 });
29469                 plan.push(key, value, params);
29470               }
29471               
29472               cycle.pop();
29473               visited[key] = VISIT_DONE;
29474             }
29475             forEach(invocables, visit);
29476             invocables = cycle = visited = null; // plan is all that's required
29477             
29478             function isResolve(value) {
29479               return isObject(value) && value.then && value.$$promises;
29480             }
29481             
29482             return function (locals, parent, self) {
29483               if (isResolve(locals) && self === undefined) {
29484                 self = parent; parent = locals; locals = null;
29485               }
29486               if (!locals) locals = NO_LOCALS;
29487               else if (!isObject(locals)) {
29488                 throw new Error("'locals' must be an object");
29489               }       
29490               if (!parent) parent = NO_PARENT;
29491               else if (!isResolve(parent)) {
29492                 throw new Error("'parent' must be a promise returned by $resolve.resolve()");
29493               }
29494               
29495               // To complete the overall resolution, we have to wait for the parent
29496               // promise and for the promise for each invokable in our plan.
29497               var resolution = $q.defer(),
29498                   result = resolution.promise,
29499                   promises = result.$$promises = {},
29500                   values = extend({}, locals),
29501                   wait = 1 + plan.length/3,
29502                   merged = false;
29503                   
29504               function done() {
29505                 // Merge parent values we haven't got yet and publish our own $$values
29506                 if (!--wait) {
29507                   if (!merged) merge(values, parent.$$values); 
29508                   result.$$values = values;
29509                   result.$$promises = result.$$promises || true; // keep for isResolve()
29510                   delete result.$$inheritedValues;
29511                   resolution.resolve(values);
29512                 }
29513               }
29514               
29515               function fail(reason) {
29516                 result.$$failure = reason;
29517                 resolution.reject(reason);
29518               }
29519
29520               // Short-circuit if parent has already failed
29521               if (isDefined(parent.$$failure)) {
29522                 fail(parent.$$failure);
29523                 return result;
29524               }
29525               
29526               if (parent.$$inheritedValues) {
29527                 merge(values, omit(parent.$$inheritedValues, invocableKeys));
29528               }
29529
29530               // Merge parent values if the parent has already resolved, or merge
29531               // parent promises and wait if the parent resolve is still in progress.
29532               extend(promises, parent.$$promises);
29533               if (parent.$$values) {
29534                 merged = merge(values, omit(parent.$$values, invocableKeys));
29535                 result.$$inheritedValues = omit(parent.$$values, invocableKeys);
29536                 done();
29537               } else {
29538                 if (parent.$$inheritedValues) {
29539                   result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
29540                 }        
29541                 parent.then(done, fail);
29542               }
29543               
29544               // Process each invocable in the plan, but ignore any where a local of the same name exists.
29545               for (var i=0, ii=plan.length; i<ii; i+=3) {
29546                 if (locals.hasOwnProperty(plan[i])) done();
29547                 else invoke(plan[i], plan[i+1], plan[i+2]);
29548               }
29549               
29550               function invoke(key, invocable, params) {
29551                 // Create a deferred for this invocation. Failures will propagate to the resolution as well.
29552                 var invocation = $q.defer(), waitParams = 0;
29553                 function onfailure(reason) {
29554                   invocation.reject(reason);
29555                   fail(reason);
29556                 }
29557                 // Wait for any parameter that we have a promise for (either from parent or from this
29558                 // resolve; in that case study() will have made sure it's ordered before us in the plan).
29559                 forEach(params, function (dep) {
29560                   if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
29561                     waitParams++;
29562                     promises[dep].then(function (result) {
29563                       values[dep] = result;
29564                       if (!(--waitParams)) proceed();
29565                     }, onfailure);
29566                   }
29567                 });
29568                 if (!waitParams) proceed();
29569                 function proceed() {
29570                   if (isDefined(result.$$failure)) return;
29571                   try {
29572                     invocation.resolve($injector.invoke(invocable, self, values));
29573                     invocation.promise.then(function (result) {
29574                       values[key] = result;
29575                       done();
29576                     }, onfailure);
29577                   } catch (e) {
29578                     onfailure(e);
29579                   }
29580                 }
29581                 // Publish promise synchronously; invocations further down in the plan may depend on it.
29582                 promises[key] = invocation.promise;
29583               }
29584               
29585               return result;
29586             };
29587           };
29588           
29589           /**
29590            * @ngdoc function
29591            * @name ui.router.util.$resolve#resolve
29592            * @methodOf ui.router.util.$resolve
29593            *
29594            * @description
29595            * Resolves a set of invocables. An invocable is a function to be invoked via 
29596            * `$injector.invoke()`, and can have an arbitrary number of dependencies. 
29597            * An invocable can either return a value directly,
29598            * or a `$q` promise. If a promise is returned it will be resolved and the 
29599            * resulting value will be used instead. Dependencies of invocables are resolved 
29600            * (in this order of precedence)
29601            *
29602            * - from the specified `locals`
29603            * - from another invocable that is part of this `$resolve` call
29604            * - from an invocable that is inherited from a `parent` call to `$resolve` 
29605            *   (or recursively
29606            * - from any ancestor `$resolve` of that parent).
29607            *
29608            * The return value of `$resolve` is a promise for an object that contains 
29609            * (in this order of precedence)
29610            *
29611            * - any `locals` (if specified)
29612            * - the resolved return values of all injectables
29613            * - any values inherited from a `parent` call to `$resolve` (if specified)
29614            *
29615            * The promise will resolve after the `parent` promise (if any) and all promises 
29616            * returned by injectables have been resolved. If any invocable 
29617            * (or `$injector.invoke`) throws an exception, or if a promise returned by an 
29618            * invocable is rejected, the `$resolve` promise is immediately rejected with the 
29619            * same error. A rejection of a `parent` promise (if specified) will likewise be 
29620            * propagated immediately. Once the `$resolve` promise has been rejected, no 
29621            * further invocables will be called.
29622            * 
29623            * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
29624            * to throw an error. As a special case, an injectable can depend on a parameter 
29625            * with the same name as the injectable, which will be fulfilled from the `parent` 
29626            * injectable of the same name. This allows inherited values to be decorated. 
29627            * Note that in this case any other injectable in the same `$resolve` with the same
29628            * dependency would see the decorated value, not the inherited value.
29629            *
29630            * Note that missing dependencies -- unlike cyclic dependencies -- will cause an 
29631            * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous) 
29632            * exception.
29633            *
29634            * Invocables are invoked eagerly as soon as all dependencies are available. 
29635            * This is true even for dependencies inherited from a `parent` call to `$resolve`.
29636            *
29637            * As a special case, an invocable can be a string, in which case it is taken to 
29638            * be a service name to be passed to `$injector.get()`. This is supported primarily 
29639            * for backwards-compatibility with the `resolve` property of `$routeProvider` 
29640            * routes.
29641            *
29642            * @param {object} invocables functions to invoke or 
29643            * `$injector` services to fetch.
29644            * @param {object} locals  values to make available to the injectables
29645            * @param {object} parent  a promise returned by another call to `$resolve`.
29646            * @param {object} self  the `this` for the invoked methods
29647            * @return {object} Promise for an object that contains the resolved return value
29648            * of all invocables, as well as any inherited and local values.
29649            */
29650           this.resolve = function (invocables, locals, parent, self) {
29651             return this.study(invocables)(locals, parent, self);
29652           };
29653         }
29654
29655         angular.module('ui.router.util').service('$resolve', $Resolve);
29656
29657
29658         /**
29659          * @ngdoc object
29660          * @name ui.router.util.$templateFactory
29661          *
29662          * @requires $http
29663          * @requires $templateCache
29664          * @requires $injector
29665          *
29666          * @description
29667          * Service. Manages loading of templates.
29668          */
29669         $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
29670         function $TemplateFactory(  $http,   $templateCache,   $injector) {
29671
29672           /**
29673            * @ngdoc function
29674            * @name ui.router.util.$templateFactory#fromConfig
29675            * @methodOf ui.router.util.$templateFactory
29676            *
29677            * @description
29678            * Creates a template from a configuration object. 
29679            *
29680            * @param {object} config Configuration object for which to load a template. 
29681            * The following properties are search in the specified order, and the first one 
29682            * that is defined is used to create the template:
29683            *
29684            * @param {string|object} config.template html string template or function to 
29685            * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29686            * @param {string|object} config.templateUrl url to load or a function returning 
29687            * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
29688            * @param {Function} config.templateProvider function to invoke via 
29689            * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
29690            * @param {object} params  Parameters to pass to the template function.
29691            * @param {object} locals Locals to pass to `invoke` if the template is loaded 
29692            * via a `templateProvider`. Defaults to `{ params: params }`.
29693            *
29694            * @return {string|object}  The template html as a string, or a promise for 
29695            * that string,or `null` if no template is configured.
29696            */
29697           this.fromConfig = function (config, params, locals) {
29698             return (
29699               isDefined(config.template) ? this.fromString(config.template, params) :
29700               isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
29701               isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
29702               null
29703             );
29704           };
29705
29706           /**
29707            * @ngdoc function
29708            * @name ui.router.util.$templateFactory#fromString
29709            * @methodOf ui.router.util.$templateFactory
29710            *
29711            * @description
29712            * Creates a template from a string or a function returning a string.
29713            *
29714            * @param {string|object} template html template as a string or function that 
29715            * returns an html template as a string.
29716            * @param {object} params Parameters to pass to the template function.
29717            *
29718            * @return {string|object} The template html as a string, or a promise for that 
29719            * string.
29720            */
29721           this.fromString = function (template, params) {
29722             return isFunction(template) ? template(params) : template;
29723           };
29724
29725           /**
29726            * @ngdoc function
29727            * @name ui.router.util.$templateFactory#fromUrl
29728            * @methodOf ui.router.util.$templateFactory
29729            * 
29730            * @description
29731            * Loads a template from the a URL via `$http` and `$templateCache`.
29732            *
29733            * @param {string|Function} url url of the template to load, or a function 
29734            * that returns a url.
29735            * @param {Object} params Parameters to pass to the url function.
29736            * @return {string|Promise.<string>} The template html as a string, or a promise 
29737            * for that string.
29738            */
29739           this.fromUrl = function (url, params) {
29740             if (isFunction(url)) url = url(params);
29741             if (url == null) return null;
29742             else return $http
29743                 .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
29744                 .then(function(response) { return response.data; });
29745           };
29746
29747           /**
29748            * @ngdoc function
29749            * @name ui.router.util.$templateFactory#fromProvider
29750            * @methodOf ui.router.util.$templateFactory
29751            *
29752            * @description
29753            * Creates a template by invoking an injectable provider function.
29754            *
29755            * @param {Function} provider Function to invoke via `$injector.invoke`
29756            * @param {Object} params Parameters for the template.
29757            * @param {Object} locals Locals to pass to `invoke`. Defaults to 
29758            * `{ params: params }`.
29759            * @return {string|Promise.<string>} The template html as a string, or a promise 
29760            * for that string.
29761            */
29762           this.fromProvider = function (provider, params, locals) {
29763             return $injector.invoke(provider, null, locals || { params: params });
29764           };
29765         }
29766
29767         angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
29768
29769         var $$UMFP; // reference to $UrlMatcherFactoryProvider
29770
29771         /**
29772          * @ngdoc object
29773          * @name ui.router.util.type:UrlMatcher
29774          *
29775          * @description
29776          * Matches URLs against patterns and extracts named parameters from the path or the search
29777          * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
29778          * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
29779          * do not influence whether or not a URL is matched, but their values are passed through into
29780          * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
29781          *
29782          * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
29783          * syntax, which optionally allows a regular expression for the parameter to be specified:
29784          *
29785          * * `':'` name - colon placeholder
29786          * * `'*'` name - catch-all placeholder
29787          * * `'{' name '}'` - curly placeholder
29788          * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
29789          *   regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
29790          *
29791          * Parameter names may contain only word characters (latin letters, digits, and underscore) and
29792          * must be unique within the pattern (across both path and search parameters). For colon
29793          * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
29794          * number of characters other than '/'. For catch-all placeholders the path parameter matches
29795          * any number of characters.
29796          *
29797          * Examples:
29798          *
29799          * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
29800          *   trailing slashes, and patterns have to match the entire path, not just a prefix.
29801          * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
29802          *   '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
29803          * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
29804          * * `'/user/{id:[^/]*}'` - Same as the previous example.
29805          * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
29806          *   parameter consists of 1 to 8 hex digits.
29807          * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
29808          *   path into the parameter 'path'.
29809          * * `'/files/*path'` - ditto.
29810          * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
29811          *   in the built-in  `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
29812          *
29813          * @param {string} pattern  The pattern to compile into a matcher.
29814          * @param {Object} config  A configuration object hash:
29815          * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
29816          *   an existing UrlMatcher
29817          *
29818          * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
29819          * * `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`.
29820          *
29821          * @property {string} prefix  A static prefix of this pattern. The matcher guarantees that any
29822          *   URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
29823          *   non-null) will start with this prefix.
29824          *
29825          * @property {string} source  The pattern that was passed into the constructor
29826          *
29827          * @property {string} sourcePath  The path portion of the source property
29828          *
29829          * @property {string} sourceSearch  The search portion of the source property
29830          *
29831          * @property {string} regex  The constructed regex that will be used to match against the url when
29832          *   it is time to determine which url will match.
29833          *
29834          * @returns {Object}  New `UrlMatcher` object
29835          */
29836         function UrlMatcher(pattern, config, parentMatcher) {
29837           config = extend({ params: {} }, isObject(config) ? config : {});
29838
29839           // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
29840           //   '*' name
29841           //   ':' name
29842           //   '{' name '}'
29843           //   '{' name ':' regexp '}'
29844           // The regular expression is somewhat complicated due to the need to allow curly braces
29845           // inside the regular expression. The placeholder regexp breaks down as follows:
29846           //    ([:*])([\w\[\]]+)              - classic placeholder ($1 / $2) (search version has - for snake-case)
29847           //    \{([\w\[\]]+)(?:\:( ... ))?\}  - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29848           //    (?: ... | ... | ... )+         - the regexp consists of any number of atoms, an atom being either
29849           //    [^{}\\]+                       - anything other than curly braces or backslash
29850           //    \\.                            - a backslash escape
29851           //    \{(?:[^{}\\]+|\\.)*\}          - a matched set of curly braces containing other atoms
29852           var placeholder       = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29853               searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29854               compiled = '^', last = 0, m,
29855               segments = this.segments = [],
29856               parentParams = parentMatcher ? parentMatcher.params : {},
29857               params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
29858               paramNames = [];
29859
29860           function addParameter(id, type, config, location) {
29861             paramNames.push(id);
29862             if (parentParams[id]) return parentParams[id];
29863             if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29864             if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
29865             params[id] = new $$UMFP.Param(id, type, config, location);
29866             return params[id];
29867           }
29868
29869           function quoteRegExp(string, pattern, squash, optional) {
29870             var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
29871             if (!pattern) return result;
29872             switch(squash) {
29873               case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
29874               case true:  surroundPattern = ['?(', ')?']; break;
29875               default:    surroundPattern = ['(' + squash + "|", ')?']; break;
29876             }
29877             return result + surroundPattern[0] + pattern + surroundPattern[1];
29878           }
29879
29880           this.source = pattern;
29881
29882           // Split into static segments separated by path parameter placeholders.
29883           // The number of segments is always 1 more than the number of parameters.
29884           function matchDetails(m, isSearch) {
29885             var id, regexp, segment, type, cfg, arrayMode;
29886             id          = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
29887             cfg         = config.params[id];
29888             segment     = pattern.substring(last, m.index);
29889             regexp      = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
29890             type        = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29891             return {
29892               id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
29893             };
29894           }
29895
29896           var p, param, segment;
29897           while ((m = placeholder.exec(pattern))) {
29898             p = matchDetails(m, false);
29899             if (p.segment.indexOf('?') >= 0) break; // we're into the search part
29900
29901             param = addParameter(p.id, p.type, p.cfg, "path");
29902             compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
29903             segments.push(p.segment);
29904             last = placeholder.lastIndex;
29905           }
29906           segment = pattern.substring(last);
29907
29908           // Find any search parameter names and remove them from the last segment
29909           var i = segment.indexOf('?');
29910
29911           if (i >= 0) {
29912             var search = this.sourceSearch = segment.substring(i);
29913             segment = segment.substring(0, i);
29914             this.sourcePath = pattern.substring(0, last + i);
29915
29916             if (search.length > 0) {
29917               last = 0;
29918               while ((m = searchPlaceholder.exec(search))) {
29919                 p = matchDetails(m, true);
29920                 param = addParameter(p.id, p.type, p.cfg, "search");
29921                 last = placeholder.lastIndex;
29922                 // check if ?&
29923               }
29924             }
29925           } else {
29926             this.sourcePath = pattern;
29927             this.sourceSearch = '';
29928           }
29929
29930           compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
29931           segments.push(segment);
29932
29933           this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
29934           this.prefix = segments[0];
29935           this.$$paramNames = paramNames;
29936         }
29937
29938         /**
29939          * @ngdoc function
29940          * @name ui.router.util.type:UrlMatcher#concat
29941          * @methodOf ui.router.util.type:UrlMatcher
29942          *
29943          * @description
29944          * Returns a new matcher for a pattern constructed by appending the path part and adding the
29945          * search parameters of the specified pattern to this pattern. The current pattern is not
29946          * modified. This can be understood as creating a pattern for URLs that are relative to (or
29947          * suffixes of) the current pattern.
29948          *
29949          * @example
29950          * The following two matchers are equivalent:
29951          * <pre>
29952          * new UrlMatcher('/user/{id}?q').concat('/details?date');
29953          * new UrlMatcher('/user/{id}/details?q&date');
29954          * </pre>
29955          *
29956          * @param {string} pattern  The pattern to append.
29957          * @param {Object} config  An object hash of the configuration for the matcher.
29958          * @returns {UrlMatcher}  A matcher for the concatenated pattern.
29959          */
29960         UrlMatcher.prototype.concat = function (pattern, config) {
29961           // Because order of search parameters is irrelevant, we can add our own search
29962           // parameters to the end of the new pattern. Parse the new pattern by itself
29963           // and then join the bits together, but it's much easier to do this on a string level.
29964           var defaultConfig = {
29965             caseInsensitive: $$UMFP.caseInsensitive(),
29966             strict: $$UMFP.strictMode(),
29967             squash: $$UMFP.defaultSquashPolicy()
29968           };
29969           return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
29970         };
29971
29972         UrlMatcher.prototype.toString = function () {
29973           return this.source;
29974         };
29975
29976         /**
29977          * @ngdoc function
29978          * @name ui.router.util.type:UrlMatcher#exec
29979          * @methodOf ui.router.util.type:UrlMatcher
29980          *
29981          * @description
29982          * Tests the specified path against this matcher, and returns an object containing the captured
29983          * parameter values, or null if the path does not match. The returned object contains the values
29984          * of any search parameters that are mentioned in the pattern, but their value may be null if
29985          * they are not present in `searchParams`. This means that search parameters are always treated
29986          * as optional.
29987          *
29988          * @example
29989          * <pre>
29990          * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
29991          *   x: '1', q: 'hello'
29992          * });
29993          * // returns { id: 'bob', q: 'hello', r: null }
29994          * </pre>
29995          *
29996          * @param {string} path  The URL path to match, e.g. `$location.path()`.
29997          * @param {Object} searchParams  URL search parameters, e.g. `$location.search()`.
29998          * @returns {Object}  The captured parameter values.
29999          */
30000         UrlMatcher.prototype.exec = function (path, searchParams) {
30001           var m = this.regexp.exec(path);
30002           if (!m) return null;
30003           searchParams = searchParams || {};
30004
30005           var paramNames = this.parameters(), nTotal = paramNames.length,
30006             nPath = this.segments.length - 1,
30007             values = {}, i, j, cfg, paramName;
30008
30009           if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
30010
30011           function decodePathArray(string) {
30012             function reverseString(str) { return str.split("").reverse().join(""); }
30013             function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
30014
30015             var split = reverseString(string).split(/-(?!\\)/);
30016             var allReversed = map(split, reverseString);
30017             return map(allReversed, unquoteDashes).reverse();
30018           }
30019
30020           for (i = 0; i < nPath; i++) {
30021             paramName = paramNames[i];
30022             var param = this.params[paramName];
30023             var paramVal = m[i+1];
30024             // if the param value matches a pre-replace pair, replace the value before decoding.
30025             for (j = 0; j < param.replace; j++) {
30026               if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30027             }
30028             if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30029             values[paramName] = param.value(paramVal);
30030           }
30031           for (/**/; i < nTotal; i++) {
30032             paramName = paramNames[i];
30033             values[paramName] = this.params[paramName].value(searchParams[paramName]);
30034           }
30035
30036           return values;
30037         };
30038
30039         /**
30040          * @ngdoc function
30041          * @name ui.router.util.type:UrlMatcher#parameters
30042          * @methodOf ui.router.util.type:UrlMatcher
30043          *
30044          * @description
30045          * Returns the names of all path and search parameters of this pattern in an unspecified order.
30046          *
30047          * @returns {Array.<string>}  An array of parameter names. Must be treated as read-only. If the
30048          *    pattern has no parameters, an empty array is returned.
30049          */
30050         UrlMatcher.prototype.parameters = function (param) {
30051           if (!isDefined(param)) return this.$$paramNames;
30052           return this.params[param] || null;
30053         };
30054
30055         /**
30056          * @ngdoc function
30057          * @name ui.router.util.type:UrlMatcher#validate
30058          * @methodOf ui.router.util.type:UrlMatcher
30059          *
30060          * @description
30061          * Checks an object hash of parameters to validate their correctness according to the parameter
30062          * types of this `UrlMatcher`.
30063          *
30064          * @param {Object} params The object hash of parameters to validate.
30065          * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
30066          */
30067         UrlMatcher.prototype.validates = function (params) {
30068           return this.params.$$validates(params);
30069         };
30070
30071         /**
30072          * @ngdoc function
30073          * @name ui.router.util.type:UrlMatcher#format
30074          * @methodOf ui.router.util.type:UrlMatcher
30075          *
30076          * @description
30077          * Creates a URL that matches this pattern by substituting the specified values
30078          * for the path and search parameters. Null values for path parameters are
30079          * treated as empty strings.
30080          *
30081          * @example
30082          * <pre>
30083          * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
30084          * // returns '/user/bob?q=yes'
30085          * </pre>
30086          *
30087          * @param {Object} values  the values to substitute for the parameters in this pattern.
30088          * @returns {string}  the formatted URL (path and optionally search part).
30089          */
30090         UrlMatcher.prototype.format = function (values) {
30091           values = values || {};
30092           var segments = this.segments, params = this.parameters(), paramset = this.params;
30093           if (!this.validates(values)) return null;
30094
30095           var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
30096
30097           function encodeDashes(str) { // Replace dashes with encoded "\-"
30098             return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
30099           }
30100
30101           for (i = 0; i < nTotal; i++) {
30102             var isPathParam = i < nPath;
30103             var name = params[i], param = paramset[name], value = param.value(values[name]);
30104             var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
30105             var squash = isDefaultValue ? param.squash : false;
30106             var encoded = param.type.encode(value);
30107
30108             if (isPathParam) {
30109               var nextSegment = segments[i + 1];
30110               if (squash === false) {
30111                 if (encoded != null) {
30112                   if (isArray(encoded)) {
30113                     result += map(encoded, encodeDashes).join("-");
30114                   } else {
30115                     result += encodeURIComponent(encoded);
30116                   }
30117                 }
30118                 result += nextSegment;
30119               } else if (squash === true) {
30120                 var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
30121                 result += nextSegment.match(capture)[1];
30122               } else if (isString(squash)) {
30123                 result += squash + nextSegment;
30124               }
30125             } else {
30126               if (encoded == null || (isDefaultValue && squash !== false)) continue;
30127               if (!isArray(encoded)) encoded = [ encoded ];
30128               encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
30129               result += (search ? '&' : '?') + (name + '=' + encoded);
30130               search = true;
30131             }
30132           }
30133
30134           return result;
30135         };
30136
30137         /**
30138          * @ngdoc object
30139          * @name ui.router.util.type:Type
30140          *
30141          * @description
30142          * Implements an interface to define custom parameter types that can be decoded from and encoded to
30143          * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
30144          * objects when matching or formatting URLs, or comparing or validating parameter values.
30145          *
30146          * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
30147          * information on registering custom types.
30148          *
30149          * @param {Object} config  A configuration object which contains the custom type definition.  The object's
30150          *        properties will override the default methods and/or pattern in `Type`'s public interface.
30151          * @example
30152          * <pre>
30153          * {
30154          *   decode: function(val) { return parseInt(val, 10); },
30155          *   encode: function(val) { return val && val.toString(); },
30156          *   equals: function(a, b) { return this.is(a) && a === b; },
30157          *   is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
30158          *   pattern: /\d+/
30159          * }
30160          * </pre>
30161          *
30162          * @property {RegExp} pattern The regular expression pattern used to match values of this type when
30163          *           coming from a substring of a URL.
30164          *
30165          * @returns {Object}  Returns a new `Type` object.
30166          */
30167         function Type(config) {
30168           extend(this, config);
30169         }
30170
30171         /**
30172          * @ngdoc function
30173          * @name ui.router.util.type:Type#is
30174          * @methodOf ui.router.util.type:Type
30175          *
30176          * @description
30177          * Detects whether a value is of a particular type. Accepts a native (decoded) value
30178          * and determines whether it matches the current `Type` object.
30179          *
30180          * @param {*} val  The value to check.
30181          * @param {string} key  Optional. If the type check is happening in the context of a specific
30182          *        {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
30183          *        parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
30184          * @returns {Boolean}  Returns `true` if the value matches the type, otherwise `false`.
30185          */
30186         Type.prototype.is = function(val, key) {
30187           return true;
30188         };
30189
30190         /**
30191          * @ngdoc function
30192          * @name ui.router.util.type:Type#encode
30193          * @methodOf ui.router.util.type:Type
30194          *
30195          * @description
30196          * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
30197          * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
30198          * only needs to be a representation of `val` that has been coerced to a string.
30199          *
30200          * @param {*} val  The value to encode.
30201          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30202          *        meta-programming of `Type` objects.
30203          * @returns {string}  Returns a string representation of `val` that can be encoded in a URL.
30204          */
30205         Type.prototype.encode = function(val, key) {
30206           return val;
30207         };
30208
30209         /**
30210          * @ngdoc function
30211          * @name ui.router.util.type:Type#decode
30212          * @methodOf ui.router.util.type:Type
30213          *
30214          * @description
30215          * Converts a parameter value (from URL string or transition param) to a custom/native value.
30216          *
30217          * @param {string} val  The URL parameter value to decode.
30218          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30219          *        meta-programming of `Type` objects.
30220          * @returns {*}  Returns a custom representation of the URL parameter value.
30221          */
30222         Type.prototype.decode = function(val, key) {
30223           return val;
30224         };
30225
30226         /**
30227          * @ngdoc function
30228          * @name ui.router.util.type:Type#equals
30229          * @methodOf ui.router.util.type:Type
30230          *
30231          * @description
30232          * Determines whether two decoded values are equivalent.
30233          *
30234          * @param {*} a  A value to compare against.
30235          * @param {*} b  A value to compare against.
30236          * @returns {Boolean}  Returns `true` if the values are equivalent/equal, otherwise `false`.
30237          */
30238         Type.prototype.equals = function(a, b) {
30239           return a == b;
30240         };
30241
30242         Type.prototype.$subPattern = function() {
30243           var sub = this.pattern.toString();
30244           return sub.substr(1, sub.length - 2);
30245         };
30246
30247         Type.prototype.pattern = /.*/;
30248
30249         Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
30250
30251         /** Given an encoded string, or a decoded object, returns a decoded object */
30252         Type.prototype.$normalize = function(val) {
30253           return this.is(val) ? val : this.decode(val);
30254         };
30255
30256         /*
30257          * Wraps an existing custom Type as an array of Type, depending on 'mode'.
30258          * e.g.:
30259          * - urlmatcher pattern "/path?{queryParam[]:int}"
30260          * - url: "/path?queryParam=1&queryParam=2
30261          * - $stateParams.queryParam will be [1, 2]
30262          * if `mode` is "auto", then
30263          * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
30264          * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
30265          */
30266         Type.prototype.$asArray = function(mode, isSearch) {
30267           if (!mode) return this;
30268           if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
30269
30270           function ArrayType(type, mode) {
30271             function bindTo(type, callbackName) {
30272               return function() {
30273                 return type[callbackName].apply(type, arguments);
30274               };
30275             }
30276
30277             // Wrap non-array value as array
30278             function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
30279             // Unwrap array value for "auto" mode. Return undefined for empty array.
30280             function arrayUnwrap(val) {
30281               switch(val.length) {
30282                 case 0: return undefined;
30283                 case 1: return mode === "auto" ? val[0] : val;
30284                 default: return val;
30285               }
30286             }
30287             function falsey(val) { return !val; }
30288
30289             // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
30290             function arrayHandler(callback, allTruthyMode) {
30291               return function handleArray(val) {
30292                 val = arrayWrap(val);
30293                 var result = map(val, callback);
30294                 if (allTruthyMode === true)
30295                   return filter(result, falsey).length === 0;
30296                 return arrayUnwrap(result);
30297               };
30298             }
30299
30300             // Wraps type (.equals) functions to operate on each value of an array
30301             function arrayEqualsHandler(callback) {
30302               return function handleArray(val1, val2) {
30303                 var left = arrayWrap(val1), right = arrayWrap(val2);
30304                 if (left.length !== right.length) return false;
30305                 for (var i = 0; i < left.length; i++) {
30306                   if (!callback(left[i], right[i])) return false;
30307                 }
30308                 return true;
30309               };
30310             }
30311
30312             this.encode = arrayHandler(bindTo(type, 'encode'));
30313             this.decode = arrayHandler(bindTo(type, 'decode'));
30314             this.is     = arrayHandler(bindTo(type, 'is'), true);
30315             this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
30316             this.pattern = type.pattern;
30317             this.$normalize = arrayHandler(bindTo(type, '$normalize'));
30318             this.name = type.name;
30319             this.$arrayMode = mode;
30320           }
30321
30322           return new ArrayType(this, mode);
30323         };
30324
30325
30326
30327         /**
30328          * @ngdoc object
30329          * @name ui.router.util.$urlMatcherFactory
30330          *
30331          * @description
30332          * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
30333          * is also available to providers under the name `$urlMatcherFactoryProvider`.
30334          */
30335         function $UrlMatcherFactory() {
30336           $$UMFP = this;
30337
30338           var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
30339
30340           function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
30341           function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
30342
30343           var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30344             string: {
30345               encode: valToString,
30346               decode: valFromString,
30347               // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
30348               // In 0.2.x, string params are optional by default for backwards compat
30349               is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
30350               pattern: /[^/]*/
30351             },
30352             int: {
30353               encode: valToString,
30354               decode: function(val) { return parseInt(val, 10); },
30355               is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
30356               pattern: /\d+/
30357             },
30358             bool: {
30359               encode: function(val) { return val ? 1 : 0; },
30360               decode: function(val) { return parseInt(val, 10) !== 0; },
30361               is: function(val) { return val === true || val === false; },
30362               pattern: /0|1/
30363             },
30364             date: {
30365               encode: function (val) {
30366                 if (!this.is(val))
30367                   return undefined;
30368                 return [ val.getFullYear(),
30369                   ('0' + (val.getMonth() + 1)).slice(-2),
30370                   ('0' + val.getDate()).slice(-2)
30371                 ].join("-");
30372               },
30373               decode: function (val) {
30374                 if (this.is(val)) return val;
30375                 var match = this.capture.exec(val);
30376                 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
30377               },
30378               is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
30379               equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
30380               pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
30381               capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
30382             },
30383             json: {
30384               encode: angular.toJson,
30385               decode: angular.fromJson,
30386               is: angular.isObject,
30387               equals: angular.equals,
30388               pattern: /[^/]*/
30389             },
30390             any: { // does not encode/decode
30391               encode: angular.identity,
30392               decode: angular.identity,
30393               equals: angular.equals,
30394               pattern: /.*/
30395             }
30396           };
30397
30398           function getDefaultConfig() {
30399             return {
30400               strict: isStrictMode,
30401               caseInsensitive: isCaseInsensitive
30402             };
30403           }
30404
30405           function isInjectable(value) {
30406             return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
30407           }
30408
30409           /**
30410            * [Internal] Get the default value of a parameter, which may be an injectable function.
30411            */
30412           $UrlMatcherFactory.$$getDefaultValue = function(config) {
30413             if (!isInjectable(config.value)) return config.value;
30414             if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30415             return injector.invoke(config.value);
30416           };
30417
30418           /**
30419            * @ngdoc function
30420            * @name ui.router.util.$urlMatcherFactory#caseInsensitive
30421            * @methodOf ui.router.util.$urlMatcherFactory
30422            *
30423            * @description
30424            * Defines whether URL matching should be case sensitive (the default behavior), or not.
30425            *
30426            * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
30427            * @returns {boolean} the current value of caseInsensitive
30428            */
30429           this.caseInsensitive = function(value) {
30430             if (isDefined(value))
30431               isCaseInsensitive = value;
30432             return isCaseInsensitive;
30433           };
30434
30435           /**
30436            * @ngdoc function
30437            * @name ui.router.util.$urlMatcherFactory#strictMode
30438            * @methodOf ui.router.util.$urlMatcherFactory
30439            *
30440            * @description
30441            * Defines whether URLs should match trailing slashes, or not (the default behavior).
30442            *
30443            * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
30444            * @returns {boolean} the current value of strictMode
30445            */
30446           this.strictMode = function(value) {
30447             if (isDefined(value))
30448               isStrictMode = value;
30449             return isStrictMode;
30450           };
30451
30452           /**
30453            * @ngdoc function
30454            * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
30455            * @methodOf ui.router.util.$urlMatcherFactory
30456            *
30457            * @description
30458            * Sets the default behavior when generating or matching URLs with default parameter values.
30459            *
30460            * @param {string} value A string that defines the default parameter URL squashing behavior.
30461            *    `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
30462            *    `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
30463            *             parameter is surrounded by slashes, squash (remove) one slash from the URL
30464            *    any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
30465            *             the parameter value from the URL and replace it with this string.
30466            */
30467           this.defaultSquashPolicy = function(value) {
30468             if (!isDefined(value)) return defaultSquashPolicy;
30469             if (value !== true && value !== false && !isString(value))
30470               throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
30471             defaultSquashPolicy = value;
30472             return value;
30473           };
30474
30475           /**
30476            * @ngdoc function
30477            * @name ui.router.util.$urlMatcherFactory#compile
30478            * @methodOf ui.router.util.$urlMatcherFactory
30479            *
30480            * @description
30481            * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
30482            *
30483            * @param {string} pattern  The URL pattern.
30484            * @param {Object} config  The config object hash.
30485            * @returns {UrlMatcher}  The UrlMatcher.
30486            */
30487           this.compile = function (pattern, config) {
30488             return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
30489           };
30490
30491           /**
30492            * @ngdoc function
30493            * @name ui.router.util.$urlMatcherFactory#isMatcher
30494            * @methodOf ui.router.util.$urlMatcherFactory
30495            *
30496            * @description
30497            * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
30498            *
30499            * @param {Object} object  The object to perform the type check against.
30500            * @returns {Boolean}  Returns `true` if the object matches the `UrlMatcher` interface, by
30501            *          implementing all the same methods.
30502            */
30503           this.isMatcher = function (o) {
30504             if (!isObject(o)) return false;
30505             var result = true;
30506
30507             forEach(UrlMatcher.prototype, function(val, name) {
30508               if (isFunction(val)) {
30509                 result = result && (isDefined(o[name]) && isFunction(o[name]));
30510               }
30511             });
30512             return result;
30513           };
30514
30515           /**
30516            * @ngdoc function
30517            * @name ui.router.util.$urlMatcherFactory#type
30518            * @methodOf ui.router.util.$urlMatcherFactory
30519            *
30520            * @description
30521            * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
30522            * generate URLs with typed parameters.
30523            *
30524            * @param {string} name  The type name.
30525            * @param {Object|Function} definition   The type definition. See
30526            *        {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30527            * @param {Object|Function} definitionFn (optional) A function that is injected before the app
30528            *        runtime starts.  The result of this function is merged into the existing `definition`.
30529            *        See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30530            *
30531            * @returns {Object}  Returns `$urlMatcherFactoryProvider`.
30532            *
30533            * @example
30534            * This is a simple example of a custom type that encodes and decodes items from an
30535            * array, using the array index as the URL-encoded value:
30536            *
30537            * <pre>
30538            * var list = ['John', 'Paul', 'George', 'Ringo'];
30539            *
30540            * $urlMatcherFactoryProvider.type('listItem', {
30541            *   encode: function(item) {
30542            *     // Represent the list item in the URL using its corresponding index
30543            *     return list.indexOf(item);
30544            *   },
30545            *   decode: function(item) {
30546            *     // Look up the list item by index
30547            *     return list[parseInt(item, 10)];
30548            *   },
30549            *   is: function(item) {
30550            *     // Ensure the item is valid by checking to see that it appears
30551            *     // in the list
30552            *     return list.indexOf(item) > -1;
30553            *   }
30554            * });
30555            *
30556            * $stateProvider.state('list', {
30557            *   url: "/list/{item:listItem}",
30558            *   controller: function($scope, $stateParams) {
30559            *     console.log($stateParams.item);
30560            *   }
30561            * });
30562            *
30563            * // ...
30564            *
30565            * // Changes URL to '/list/3', logs "Ringo" to the console
30566            * $state.go('list', { item: "Ringo" });
30567            * </pre>
30568            *
30569            * This is a more complex example of a type that relies on dependency injection to
30570            * interact with services, and uses the parameter name from the URL to infer how to
30571            * handle encoding and decoding parameter values:
30572            *
30573            * <pre>
30574            * // Defines a custom type that gets a value from a service,
30575            * // where each service gets different types of values from
30576            * // a backend API:
30577            * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
30578            *
30579            *   // Matches up services to URL parameter names
30580            *   var services = {
30581            *     user: Users,
30582            *     post: Posts
30583            *   };
30584            *
30585            *   return {
30586            *     encode: function(object) {
30587            *       // Represent the object in the URL using its unique ID
30588            *       return object.id;
30589            *     },
30590            *     decode: function(value, key) {
30591            *       // Look up the object by ID, using the parameter
30592            *       // name (key) to call the correct service
30593            *       return services[key].findById(value);
30594            *     },
30595            *     is: function(object, key) {
30596            *       // Check that object is a valid dbObject
30597            *       return angular.isObject(object) && object.id && services[key];
30598            *     }
30599            *     equals: function(a, b) {
30600            *       // Check the equality of decoded objects by comparing
30601            *       // their unique IDs
30602            *       return a.id === b.id;
30603            *     }
30604            *   };
30605            * });
30606            *
30607            * // In a config() block, you can then attach URLs with
30608            * // type-annotated parameters:
30609            * $stateProvider.state('users', {
30610            *   url: "/users",
30611            *   // ...
30612            * }).state('users.item', {
30613            *   url: "/{user:dbObject}",
30614            *   controller: function($scope, $stateParams) {
30615            *     // $stateParams.user will now be an object returned from
30616            *     // the Users service
30617            *   },
30618            *   // ...
30619            * });
30620            * </pre>
30621            */
30622           this.type = function (name, definition, definitionFn) {
30623             if (!isDefined(definition)) return $types[name];
30624             if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
30625
30626             $types[name] = new Type(extend({ name: name }, definition));
30627             if (definitionFn) {
30628               typeQueue.push({ name: name, def: definitionFn });
30629               if (!enqueue) flushTypeQueue();
30630             }
30631             return this;
30632           };
30633
30634           // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
30635           function flushTypeQueue() {
30636             while(typeQueue.length) {
30637               var type = typeQueue.shift();
30638               if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
30639               angular.extend($types[type.name], injector.invoke(type.def));
30640             }
30641           }
30642
30643           // Register default types. Store them in the prototype of $types.
30644           forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
30645           $types = inherit($types, {});
30646
30647           /* No need to document $get, since it returns this */
30648           this.$get = ['$injector', function ($injector) {
30649             injector = $injector;
30650             enqueue = false;
30651             flushTypeQueue();
30652
30653             forEach(defaultTypes, function(type, name) {
30654               if (!$types[name]) $types[name] = new Type(type);
30655             });
30656             return this;
30657           }];
30658
30659           this.Param = function Param(id, type, config, location) {
30660             var self = this;
30661             config = unwrapShorthand(config);
30662             type = getType(config, type, location);
30663             var arrayMode = getArrayMode();
30664             type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
30665             if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
30666               config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
30667             var isOptional = config.value !== undefined;
30668             var squash = getSquashPolicy(config, isOptional);
30669             var replace = getReplace(config, arrayMode, isOptional, squash);
30670
30671             function unwrapShorthand(config) {
30672               var keys = isObject(config) ? objectKeys(config) : [];
30673               var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
30674                                 indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
30675               if (isShorthand) config = { value: config };
30676               config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
30677               return config;
30678             }
30679
30680             function getType(config, urlType, location) {
30681               if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
30682               if (urlType) return urlType;
30683               if (!config.type) return (location === "config" ? $types.any : $types.string);
30684               return config.type instanceof Type ? config.type : new Type(config.type);
30685             }
30686
30687             // array config: param name (param[]) overrides default settings.  explicit config overrides param name.
30688             function getArrayMode() {
30689               var arrayDefaults = { array: (location === "search" ? "auto" : false) };
30690               var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
30691               return extend(arrayDefaults, arrayParamNomenclature, config).array;
30692             }
30693
30694             /**
30695              * returns false, true, or the squash value to indicate the "default parameter url squash policy".
30696              */
30697             function getSquashPolicy(config, isOptional) {
30698               var squash = config.squash;
30699               if (!isOptional || squash === false) return false;
30700               if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
30701               if (squash === true || isString(squash)) return squash;
30702               throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
30703             }
30704
30705             function getReplace(config, arrayMode, isOptional, squash) {
30706               var replace, configuredKeys, defaultPolicy = [
30707                 { from: "",   to: (isOptional || arrayMode ? undefined : "") },
30708                 { from: null, to: (isOptional || arrayMode ? undefined : "") }
30709               ];
30710               replace = isArray(config.replace) ? config.replace : [];
30711               if (isString(squash))
30712                 replace.push({ from: squash, to: undefined });
30713               configuredKeys = map(replace, function(item) { return item.from; } );
30714               return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
30715             }
30716
30717             /**
30718              * [Internal] Get the default value of a parameter, which may be an injectable function.
30719              */
30720             function $$getDefaultValue() {
30721               if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30722               var defaultValue = injector.invoke(config.$$fn);
30723               if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
30724                 throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
30725               return defaultValue;
30726             }
30727
30728             /**
30729              * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
30730              * default value, which may be the result of an injectable function.
30731              */
30732             function $value(value) {
30733               function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
30734               function $replace(value) {
30735                 var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
30736                 return replacement.length ? replacement[0] : value;
30737               }
30738               value = $replace(value);
30739               return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
30740             }
30741
30742             function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
30743
30744             extend(this, {
30745               id: id,
30746               type: type,
30747               location: location,
30748               array: arrayMode,
30749               squash: squash,
30750               replace: replace,
30751               isOptional: isOptional,
30752               value: $value,
30753               dynamic: undefined,
30754               config: config,
30755               toString: toString
30756             });
30757           };
30758
30759           function ParamSet(params) {
30760             extend(this, params || {});
30761           }
30762
30763           ParamSet.prototype = {
30764             $$new: function() {
30765               return inherit(this, extend(new ParamSet(), { $$parent: this}));
30766             },
30767             $$keys: function () {
30768               var keys = [], chain = [], parent = this,
30769                 ignore = objectKeys(ParamSet.prototype);
30770               while (parent) { chain.push(parent); parent = parent.$$parent; }
30771               chain.reverse();
30772               forEach(chain, function(paramset) {
30773                 forEach(objectKeys(paramset), function(key) {
30774                     if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
30775                 });
30776               });
30777               return keys;
30778             },
30779             $$values: function(paramValues) {
30780               var values = {}, self = this;
30781               forEach(self.$$keys(), function(key) {
30782                 values[key] = self[key].value(paramValues && paramValues[key]);
30783               });
30784               return values;
30785             },
30786             $$equals: function(paramValues1, paramValues2) {
30787               var equal = true, self = this;
30788               forEach(self.$$keys(), function(key) {
30789                 var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
30790                 if (!self[key].type.equals(left, right)) equal = false;
30791               });
30792               return equal;
30793             },
30794             $$validates: function $$validate(paramValues) {
30795               var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
30796               for (i = 0; i < keys.length; i++) {
30797                 param = this[keys[i]];
30798                 rawVal = paramValues[keys[i]];
30799                 if ((rawVal === undefined || rawVal === null) && param.isOptional)
30800                   break; // There was no parameter value, but the param is optional
30801                 normalized = param.type.$normalize(rawVal);
30802                 if (!param.type.is(normalized))
30803                   return false; // The value was not of the correct Type, and could not be decoded to the correct Type
30804                 encoded = param.type.encode(normalized);
30805                 if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
30806                   return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
30807               }
30808               return true;
30809             },
30810             $$parent: undefined
30811           };
30812
30813           this.ParamSet = ParamSet;
30814         }
30815
30816         // Register as a provider so it's available to other providers
30817         angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
30818         angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
30819
30820         /**
30821          * @ngdoc object
30822          * @name ui.router.router.$urlRouterProvider
30823          *
30824          * @requires ui.router.util.$urlMatcherFactoryProvider
30825          * @requires $locationProvider
30826          *
30827          * @description
30828          * `$urlRouterProvider` has the responsibility of watching `$location`. 
30829          * When `$location` changes it runs through a list of rules one by one until a 
30830          * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 
30831          * a url in a state configuration. All urls are compiled into a UrlMatcher object.
30832          *
30833          * There are several methods on `$urlRouterProvider` that make it useful to use directly
30834          * in your module config.
30835          */
30836         $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
30837         function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
30838           var rules = [], otherwise = null, interceptDeferred = false, listener;
30839
30840           // Returns a string that is a prefix of all strings matching the RegExp
30841           function regExpPrefix(re) {
30842             var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
30843             return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
30844           }
30845
30846           // Interpolates matched values into a String.replace()-style pattern
30847           function interpolate(pattern, match) {
30848             return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30849               return match[what === '$' ? 0 : Number(what)];
30850             });
30851           }
30852
30853           /**
30854            * @ngdoc function
30855            * @name ui.router.router.$urlRouterProvider#rule
30856            * @methodOf ui.router.router.$urlRouterProvider
30857            *
30858            * @description
30859            * Defines rules that are used by `$urlRouterProvider` to find matches for
30860            * specific URLs.
30861            *
30862            * @example
30863            * <pre>
30864            * var app = angular.module('app', ['ui.router.router']);
30865            *
30866            * app.config(function ($urlRouterProvider) {
30867            *   // Here's an example of how you might allow case insensitive urls
30868            *   $urlRouterProvider.rule(function ($injector, $location) {
30869            *     var path = $location.path(),
30870            *         normalized = path.toLowerCase();
30871            *
30872            *     if (path !== normalized) {
30873            *       return normalized;
30874            *     }
30875            *   });
30876            * });
30877            * </pre>
30878            *
30879            * @param {object} rule Handler function that takes `$injector` and `$location`
30880            * services as arguments. You can use them to return a valid path as a string.
30881            *
30882            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30883            */
30884           this.rule = function (rule) {
30885             if (!isFunction(rule)) throw new Error("'rule' must be a function");
30886             rules.push(rule);
30887             return this;
30888           };
30889
30890           /**
30891            * @ngdoc object
30892            * @name ui.router.router.$urlRouterProvider#otherwise
30893            * @methodOf ui.router.router.$urlRouterProvider
30894            *
30895            * @description
30896            * Defines a path that is used when an invalid route is requested.
30897            *
30898            * @example
30899            * <pre>
30900            * var app = angular.module('app', ['ui.router.router']);
30901            *
30902            * app.config(function ($urlRouterProvider) {
30903            *   // if the path doesn't match any of the urls you configured
30904            *   // otherwise will take care of routing the user to the
30905            *   // specified url
30906            *   $urlRouterProvider.otherwise('/index');
30907            *
30908            *   // Example of using function rule as param
30909            *   $urlRouterProvider.otherwise(function ($injector, $location) {
30910            *     return '/a/valid/url';
30911            *   });
30912            * });
30913            * </pre>
30914            *
30915            * @param {string|object} rule The url path you want to redirect to or a function 
30916            * rule that returns the url path. The function version is passed two params: 
30917            * `$injector` and `$location` services, and must return a url string.
30918            *
30919            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30920            */
30921           this.otherwise = function (rule) {
30922             if (isString(rule)) {
30923               var redirect = rule;
30924               rule = function () { return redirect; };
30925             }
30926             else if (!isFunction(rule)) throw new Error("'rule' must be a function");
30927             otherwise = rule;
30928             return this;
30929           };
30930
30931
30932           function handleIfMatch($injector, handler, match) {
30933             if (!match) return false;
30934             var result = $injector.invoke(handler, handler, { $match: match });
30935             return isDefined(result) ? result : true;
30936           }
30937
30938           /**
30939            * @ngdoc function
30940            * @name ui.router.router.$urlRouterProvider#when
30941            * @methodOf ui.router.router.$urlRouterProvider
30942            *
30943            * @description
30944            * Registers a handler for a given url matching. if handle is a string, it is
30945            * treated as a redirect, and is interpolated according to the syntax of match
30946            * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
30947            *
30948            * If the handler is a function, it is injectable. It gets invoked if `$location`
30949            * matches. You have the option of inject the match object as `$match`.
30950            *
30951            * The handler can return
30952            *
30953            * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
30954            *   will continue trying to find another one that matches.
30955            * - **string** which is treated as a redirect and passed to `$location.url()`
30956            * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
30957            *
30958            * @example
30959            * <pre>
30960            * var app = angular.module('app', ['ui.router.router']);
30961            *
30962            * app.config(function ($urlRouterProvider) {
30963            *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
30964            *     if ($state.$current.navigable !== state ||
30965            *         !equalForKeys($match, $stateParams) {
30966            *      $state.transitionTo(state, $match, false);
30967            *     }
30968            *   });
30969            * });
30970            * </pre>
30971            *
30972            * @param {string|object} what The incoming path that you want to redirect.
30973            * @param {string|object} handler The path you want to redirect your user to.
30974            */
30975           this.when = function (what, handler) {
30976             var redirect, handlerIsString = isString(handler);
30977             if (isString(what)) what = $urlMatcherFactory.compile(what);
30978
30979             if (!handlerIsString && !isFunction(handler) && !isArray(handler))
30980               throw new Error("invalid 'handler' in when()");
30981
30982             var strategies = {
30983               matcher: function (what, handler) {
30984                 if (handlerIsString) {
30985                   redirect = $urlMatcherFactory.compile(handler);
30986                   handler = ['$match', function ($match) { return redirect.format($match); }];
30987                 }
30988                 return extend(function ($injector, $location) {
30989                   return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
30990                 }, {
30991                   prefix: isString(what.prefix) ? what.prefix : ''
30992                 });
30993               },
30994               regex: function (what, handler) {
30995                 if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
30996
30997                 if (handlerIsString) {
30998                   redirect = handler;
30999                   handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
31000                 }
31001                 return extend(function ($injector, $location) {
31002                   return handleIfMatch($injector, handler, what.exec($location.path()));
31003                 }, {
31004                   prefix: regExpPrefix(what)
31005                 });
31006               }
31007             };
31008
31009             var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
31010
31011             for (var n in check) {
31012               if (check[n]) return this.rule(strategies[n](what, handler));
31013             }
31014
31015             throw new Error("invalid 'what' in when()");
31016           };
31017
31018           /**
31019            * @ngdoc function
31020            * @name ui.router.router.$urlRouterProvider#deferIntercept
31021            * @methodOf ui.router.router.$urlRouterProvider
31022            *
31023            * @description
31024            * Disables (or enables) deferring location change interception.
31025            *
31026            * If you wish to customize the behavior of syncing the URL (for example, if you wish to
31027            * defer a transition but maintain the current URL), call this method at configuration time.
31028            * Then, at run time, call `$urlRouter.listen()` after you have configured your own
31029            * `$locationChangeSuccess` event handler.
31030            *
31031            * @example
31032            * <pre>
31033            * var app = angular.module('app', ['ui.router.router']);
31034            *
31035            * app.config(function ($urlRouterProvider) {
31036            *
31037            *   // Prevent $urlRouter from automatically intercepting URL changes;
31038            *   // this allows you to configure custom behavior in between
31039            *   // location changes and route synchronization:
31040            *   $urlRouterProvider.deferIntercept();
31041            *
31042            * }).run(function ($rootScope, $urlRouter, UserService) {
31043            *
31044            *   $rootScope.$on('$locationChangeSuccess', function(e) {
31045            *     // UserService is an example service for managing user state
31046            *     if (UserService.isLoggedIn()) return;
31047            *
31048            *     // Prevent $urlRouter's default handler from firing
31049            *     e.preventDefault();
31050            *
31051            *     UserService.handleLogin().then(function() {
31052            *       // Once the user has logged in, sync the current URL
31053            *       // to the router:
31054            *       $urlRouter.sync();
31055            *     });
31056            *   });
31057            *
31058            *   // Configures $urlRouter's listener *after* your custom listener
31059            *   $urlRouter.listen();
31060            * });
31061            * </pre>
31062            *
31063            * @param {boolean} defer Indicates whether to defer location change interception. Passing
31064                     no parameter is equivalent to `true`.
31065            */
31066           this.deferIntercept = function (defer) {
31067             if (defer === undefined) defer = true;
31068             interceptDeferred = defer;
31069           };
31070
31071           /**
31072            * @ngdoc object
31073            * @name ui.router.router.$urlRouter
31074            *
31075            * @requires $location
31076            * @requires $rootScope
31077            * @requires $injector
31078            * @requires $browser
31079            *
31080            * @description
31081            *
31082            */
31083           this.$get = $get;
31084           $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
31085           function $get(   $location,   $rootScope,   $injector,   $browser) {
31086
31087             var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
31088
31089             function appendBasePath(url, isHtml5, absolute) {
31090               if (baseHref === '/') return url;
31091               if (isHtml5) return baseHref.slice(0, -1) + url;
31092               if (absolute) return baseHref.slice(1) + url;
31093               return url;
31094             }
31095
31096             // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
31097             function update(evt) {
31098               if (evt && evt.defaultPrevented) return;
31099               var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
31100               lastPushedUrl = undefined;
31101               // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
31102               //if (ignoreUpdate) return true;
31103
31104               function check(rule) {
31105                 var handled = rule($injector, $location);
31106
31107                 if (!handled) return false;
31108                 if (isString(handled)) $location.replace().url(handled);
31109                 return true;
31110               }
31111               var n = rules.length, i;
31112
31113               for (i = 0; i < n; i++) {
31114                 if (check(rules[i])) return;
31115               }
31116               // always check otherwise last to allow dynamic updates to the set of rules
31117               if (otherwise) check(otherwise);
31118             }
31119
31120             function listen() {
31121               listener = listener || $rootScope.$on('$locationChangeSuccess', update);
31122               return listener;
31123             }
31124
31125             if (!interceptDeferred) listen();
31126
31127             return {
31128               /**
31129                * @ngdoc function
31130                * @name ui.router.router.$urlRouter#sync
31131                * @methodOf ui.router.router.$urlRouter
31132                *
31133                * @description
31134                * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
31135                * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
31136                * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
31137                * with the transition by calling `$urlRouter.sync()`.
31138                *
31139                * @example
31140                * <pre>
31141                * angular.module('app', ['ui.router'])
31142                *   .run(function($rootScope, $urlRouter) {
31143                *     $rootScope.$on('$locationChangeSuccess', function(evt) {
31144                *       // Halt state change from even starting
31145                *       evt.preventDefault();
31146                *       // Perform custom logic
31147                *       var meetsRequirement = ...
31148                *       // Continue with the update and state transition if logic allows
31149                *       if (meetsRequirement) $urlRouter.sync();
31150                *     });
31151                * });
31152                * </pre>
31153                */
31154               sync: function() {
31155                 update();
31156               },
31157
31158               listen: function() {
31159                 return listen();
31160               },
31161
31162               update: function(read) {
31163                 if (read) {
31164                   location = $location.url();
31165                   return;
31166                 }
31167                 if ($location.url() === location) return;
31168
31169                 $location.url(location);
31170                 $location.replace();
31171               },
31172
31173               push: function(urlMatcher, params, options) {
31174                  var url = urlMatcher.format(params || {});
31175
31176                 // Handle the special hash param, if needed
31177                 if (url !== null && params && params['#']) {
31178                     url += '#' + params['#'];
31179                 }
31180
31181                 $location.url(url);
31182                 lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
31183                 if (options && options.replace) $location.replace();
31184               },
31185
31186               /**
31187                * @ngdoc function
31188                * @name ui.router.router.$urlRouter#href
31189                * @methodOf ui.router.router.$urlRouter
31190                *
31191                * @description
31192                * A URL generation method that returns the compiled URL for a given
31193                * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
31194                *
31195                * @example
31196                * <pre>
31197                * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
31198                *   person: "bob"
31199                * });
31200                * // $bob == "/about/bob";
31201                * </pre>
31202                *
31203                * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
31204                * @param {object=} params An object of parameter values to fill the matcher's required parameters.
31205                * @param {object=} options Options object. The options are:
31206                *
31207                * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
31208                *
31209                * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
31210                */
31211               href: function(urlMatcher, params, options) {
31212                 if (!urlMatcher.validates(params)) return null;
31213
31214                 var isHtml5 = $locationProvider.html5Mode();
31215                 if (angular.isObject(isHtml5)) {
31216                   isHtml5 = isHtml5.enabled;
31217                 }
31218                 
31219                 var url = urlMatcher.format(params);
31220                 options = options || {};
31221
31222                 if (!isHtml5 && url !== null) {
31223                   url = "#" + $locationProvider.hashPrefix() + url;
31224                 }
31225
31226                 // Handle special hash param, if needed
31227                 if (url !== null && params && params['#']) {
31228                   url += '#' + params['#'];
31229                 }
31230
31231                 url = appendBasePath(url, isHtml5, options.absolute);
31232
31233                 if (!options.absolute || !url) {
31234                   return url;
31235                 }
31236
31237                 var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
31238                 port = (port === 80 || port === 443 ? '' : ':' + port);
31239
31240                 return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
31241               }
31242             };
31243           }
31244         }
31245
31246         angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
31247
31248         /**
31249          * @ngdoc object
31250          * @name ui.router.state.$stateProvider
31251          *
31252          * @requires ui.router.router.$urlRouterProvider
31253          * @requires ui.router.util.$urlMatcherFactoryProvider
31254          *
31255          * @description
31256          * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
31257          * on state.
31258          *
31259          * A state corresponds to a "place" in the application in terms of the overall UI and
31260          * navigation. A state describes (via the controller / template / view properties) what
31261          * the UI looks like and does at that place.
31262          *
31263          * States often have things in common, and the primary way of factoring out these
31264          * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
31265          * nested states.
31266          *
31267          * The `$stateProvider` provides interfaces to declare these states for your app.
31268          */
31269         $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
31270         function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
31271
31272           var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
31273
31274           // Builds state properties from definition passed to registerState()
31275           var stateBuilder = {
31276
31277             // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31278             // state.children = [];
31279             // if (parent) parent.children.push(state);
31280             parent: function(state) {
31281               if (isDefined(state.parent) && state.parent) return findState(state.parent);
31282               // regex matches any valid composite state name
31283               // would match "contact.list" but not "contacts"
31284               var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
31285               return compositeName ? findState(compositeName[1]) : root;
31286             },
31287
31288             // inherit 'data' from parent and override by own values (if any)
31289             data: function(state) {
31290               if (state.parent && state.parent.data) {
31291                 state.data = state.self.data = extend({}, state.parent.data, state.data);
31292               }
31293               return state.data;
31294             },
31295
31296             // Build a URLMatcher if necessary, either via a relative or absolute URL
31297             url: function(state) {
31298               var url = state.url, config = { params: state.params || {} };
31299
31300               if (isString(url)) {
31301                 if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
31302                 return (state.parent.navigable || root).url.concat(url, config);
31303               }
31304
31305               if (!url || $urlMatcherFactory.isMatcher(url)) return url;
31306               throw new Error("Invalid url '" + url + "' in state '" + state + "'");
31307             },
31308
31309             // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
31310             navigable: function(state) {
31311               return state.url ? state : (state.parent ? state.parent.navigable : null);
31312             },
31313
31314             // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
31315             ownParams: function(state) {
31316               var params = state.url && state.url.params || new $$UMFP.ParamSet();
31317               forEach(state.params || {}, function(config, id) {
31318                 if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
31319               });
31320               return params;
31321             },
31322
31323             // Derive parameters for this state and ensure they're a super-set of parent's parameters
31324             params: function(state) {
31325               return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
31326             },
31327
31328             // If there is no explicit multi-view configuration, make one up so we don't have
31329             // to handle both cases in the view directive later. Note that having an explicit
31330             // 'views' property will mean the default unnamed view properties are ignored. This
31331             // is also a good time to resolve view names to absolute names, so everything is a
31332             // straight lookup at link time.
31333             views: function(state) {
31334               var views = {};
31335
31336               forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
31337                 if (name.indexOf('@') < 0) name += '@' + state.parent.name;
31338                 views[name] = view;
31339               });
31340               return views;
31341             },
31342
31343             // Keep a full path from the root down to this state as this is needed for state activation.
31344             path: function(state) {
31345               return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
31346             },
31347
31348             // Speed up $state.contains() as it's used a lot
31349             includes: function(state) {
31350               var includes = state.parent ? extend({}, state.parent.includes) : {};
31351               includes[state.name] = true;
31352               return includes;
31353             },
31354
31355             $delegates: {}
31356           };
31357
31358           function isRelative(stateName) {
31359             return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
31360           }
31361
31362           function findState(stateOrName, base) {
31363             if (!stateOrName) return undefined;
31364
31365             var isStr = isString(stateOrName),
31366                 name  = isStr ? stateOrName : stateOrName.name,
31367                 path  = isRelative(name);
31368
31369             if (path) {
31370               if (!base) throw new Error("No reference point given for path '"  + name + "'");
31371               base = findState(base);
31372               
31373               var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
31374
31375               for (; i < pathLength; i++) {
31376                 if (rel[i] === "" && i === 0) {
31377                   current = base;
31378                   continue;
31379                 }
31380                 if (rel[i] === "^") {
31381                   if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
31382                   current = current.parent;
31383                   continue;
31384                 }
31385                 break;
31386               }
31387               rel = rel.slice(i).join(".");
31388               name = current.name + (current.name && rel ? "." : "") + rel;
31389             }
31390             var state = states[name];
31391
31392             if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
31393               return state;
31394             }
31395             return undefined;
31396           }
31397
31398           function queueState(parentName, state) {
31399             if (!queue[parentName]) {
31400               queue[parentName] = [];
31401             }
31402             queue[parentName].push(state);
31403           }
31404
31405           function flushQueuedChildren(parentName) {
31406             var queued = queue[parentName] || [];
31407             while(queued.length) {
31408               registerState(queued.shift());
31409             }
31410           }
31411
31412           function registerState(state) {
31413             // Wrap a new object around the state so we can store our private details easily.
31414             state = inherit(state, {
31415               self: state,
31416               resolve: state.resolve || {},
31417               toString: function() { return this.name; }
31418             });
31419
31420             var name = state.name;
31421             if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
31422             if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
31423
31424             // Get parent name
31425             var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
31426                 : (isString(state.parent)) ? state.parent
31427                 : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
31428                 : '';
31429
31430             // If parent is not registered yet, add state to queue and register later
31431             if (parentName && !states[parentName]) {
31432               return queueState(parentName, state.self);
31433             }
31434
31435             for (var key in stateBuilder) {
31436               if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
31437             }
31438             states[name] = state;
31439
31440             // Register the state in the global state list and with $urlRouter if necessary.
31441             if (!state[abstractKey] && state.url) {
31442               $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
31443                 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
31444                   $state.transitionTo(state, $match, { inherit: true, location: false });
31445                 }
31446               }]);
31447             }
31448
31449             // Register any queued children
31450             flushQueuedChildren(name);
31451
31452             return state;
31453           }
31454
31455           // Checks text to see if it looks like a glob.
31456           function isGlob (text) {
31457             return text.indexOf('*') > -1;
31458           }
31459
31460           // Returns true if glob matches current $state name.
31461           function doesStateMatchGlob (glob) {
31462             var globSegments = glob.split('.'),
31463                 segments = $state.$current.name.split('.');
31464
31465             //match single stars
31466             for (var i = 0, l = globSegments.length; i < l; i++) {
31467               if (globSegments[i] === '*') {
31468                 segments[i] = '*';
31469               }
31470             }
31471
31472             //match greedy starts
31473             if (globSegments[0] === '**') {
31474                segments = segments.slice(indexOf(segments, globSegments[1]));
31475                segments.unshift('**');
31476             }
31477             //match greedy ends
31478             if (globSegments[globSegments.length - 1] === '**') {
31479                segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
31480                segments.push('**');
31481             }
31482
31483             if (globSegments.length != segments.length) {
31484               return false;
31485             }
31486
31487             return segments.join('') === globSegments.join('');
31488           }
31489
31490
31491           // Implicit root state that is always active
31492           root = registerState({
31493             name: '',
31494             url: '^',
31495             views: null,
31496             'abstract': true
31497           });
31498           root.navigable = null;
31499
31500
31501           /**
31502            * @ngdoc function
31503            * @name ui.router.state.$stateProvider#decorator
31504            * @methodOf ui.router.state.$stateProvider
31505            *
31506            * @description
31507            * Allows you to extend (carefully) or override (at your own peril) the 
31508            * `stateBuilder` object used internally by `$stateProvider`. This can be used 
31509            * to add custom functionality to ui-router, for example inferring templateUrl 
31510            * based on the state name.
31511            *
31512            * When passing only a name, it returns the current (original or decorated) builder
31513            * function that matches `name`.
31514            *
31515            * The builder functions that can be decorated are listed below. Though not all
31516            * necessarily have a good use case for decoration, that is up to you to decide.
31517            *
31518            * In addition, users can attach custom decorators, which will generate new 
31519            * properties within the state's internal definition. There is currently no clear 
31520            * use-case for this beyond accessing internal states (i.e. $state.$current), 
31521            * however, expect this to become increasingly relevant as we introduce additional 
31522            * meta-programming features.
31523            *
31524            * **Warning**: Decorators should not be interdependent because the order of 
31525            * execution of the builder functions in non-deterministic. Builder functions 
31526            * should only be dependent on the state definition object and super function.
31527            *
31528            *
31529            * Existing builder functions and current return values:
31530            *
31531            * - **parent** `{object}` - returns the parent state object.
31532            * - **data** `{object}` - returns state data, including any inherited data that is not
31533            *   overridden by own values (if any).
31534            * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
31535            *   or `null`.
31536            * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is 
31537            *   navigable).
31538            * - **params** `{object}` - returns an array of state params that are ensured to 
31539            *   be a super-set of parent's params.
31540            * - **views** `{object}` - returns a views object where each key is an absolute view 
31541            *   name (i.e. "viewName@stateName") and each value is the config object 
31542            *   (template, controller) for the view. Even when you don't use the views object 
31543            *   explicitly on a state config, one is still created for you internally.
31544            *   So by decorating this builder function you have access to decorating template 
31545            *   and controller properties.
31546            * - **ownParams** `{object}` - returns an array of params that belong to the state, 
31547            *   not including any params defined by ancestor states.
31548            * - **path** `{string}` - returns the full path from the root down to this state. 
31549            *   Needed for state activation.
31550            * - **includes** `{object}` - returns an object that includes every state that 
31551            *   would pass a `$state.includes()` test.
31552            *
31553            * @example
31554            * <pre>
31555            * // Override the internal 'views' builder with a function that takes the state
31556            * // definition, and a reference to the internal function being overridden:
31557            * $stateProvider.decorator('views', function (state, parent) {
31558            *   var result = {},
31559            *       views = parent(state);
31560            *
31561            *   angular.forEach(views, function (config, name) {
31562            *     var autoName = (state.name + '.' + name).replace('.', '/');
31563            *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
31564            *     result[name] = config;
31565            *   });
31566            *   return result;
31567            * });
31568            *
31569            * $stateProvider.state('home', {
31570            *   views: {
31571            *     'contact.list': { controller: 'ListController' },
31572            *     'contact.item': { controller: 'ItemController' }
31573            *   }
31574            * });
31575            *
31576            * // ...
31577            *
31578            * $state.go('home');
31579            * // Auto-populates list and item views with /partials/home/contact/list.html,
31580            * // and /partials/home/contact/item.html, respectively.
31581            * </pre>
31582            *
31583            * @param {string} name The name of the builder function to decorate. 
31584            * @param {object} func A function that is responsible for decorating the original 
31585            * builder function. The function receives two parameters:
31586            *
31587            *   - `{object}` - state - The state config object.
31588            *   - `{object}` - super - The original builder function.
31589            *
31590            * @return {object} $stateProvider - $stateProvider instance
31591            */
31592           this.decorator = decorator;
31593           function decorator(name, func) {
31594             /*jshint validthis: true */
31595             if (isString(name) && !isDefined(func)) {
31596               return stateBuilder[name];
31597             }
31598             if (!isFunction(func) || !isString(name)) {
31599               return this;
31600             }
31601             if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
31602               stateBuilder.$delegates[name] = stateBuilder[name];
31603             }
31604             stateBuilder[name] = func;
31605             return this;
31606           }
31607
31608           /**
31609            * @ngdoc function
31610            * @name ui.router.state.$stateProvider#state
31611            * @methodOf ui.router.state.$stateProvider
31612            *
31613            * @description
31614            * Registers a state configuration under a given state name. The stateConfig object
31615            * has the following acceptable properties.
31616            *
31617            * @param {string} name A unique state name, e.g. "home", "about", "contacts".
31618            * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
31619            * @param {object} stateConfig State configuration object.
31620            * @param {string|function=} stateConfig.template
31621            * <a id='template'></a>
31622            *   html template as a string or a function that returns
31623            *   an html template as a string which should be used by the uiView directives. This property 
31624            *   takes precedence over templateUrl.
31625            *   
31626            *   If `template` is a function, it will be called with the following parameters:
31627            *
31628            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
31629            *     applying the current state
31630            *
31631            * <pre>template:
31632            *   "<h1>inline template definition</h1>" +
31633            *   "<div ui-view></div>"</pre>
31634            * <pre>template: function(params) {
31635            *       return "<h1>generated template</h1>"; }</pre>
31636            * </div>
31637            *
31638            * @param {string|function=} stateConfig.templateUrl
31639            * <a id='templateUrl'></a>
31640            *
31641            *   path or function that returns a path to an html
31642            *   template that should be used by uiView.
31643            *   
31644            *   If `templateUrl` is a function, it will be called with the following parameters:
31645            *
31646            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by 
31647            *     applying the current state
31648            *
31649            * <pre>templateUrl: "home.html"</pre>
31650            * <pre>templateUrl: function(params) {
31651            *     return myTemplates[params.pageId]; }</pre>
31652            *
31653            * @param {function=} stateConfig.templateProvider
31654            * <a id='templateProvider'></a>
31655            *    Provider function that returns HTML content string.
31656            * <pre> templateProvider:
31657            *       function(MyTemplateService, params) {
31658            *         return MyTemplateService.getTemplate(params.pageId);
31659            *       }</pre>
31660            *
31661            * @param {string|function=} stateConfig.controller
31662            * <a id='controller'></a>
31663            *
31664            *  Controller fn that should be associated with newly
31665            *   related scope or the name of a registered controller if passed as a string.
31666            *   Optionally, the ControllerAs may be declared here.
31667            * <pre>controller: "MyRegisteredController"</pre>
31668            * <pre>controller:
31669            *     "MyRegisteredController as fooCtrl"}</pre>
31670            * <pre>controller: function($scope, MyService) {
31671            *     $scope.data = MyService.getData(); }</pre>
31672            *
31673            * @param {function=} stateConfig.controllerProvider
31674            * <a id='controllerProvider'></a>
31675            *
31676            * Injectable provider function that returns the actual controller or string.
31677            * <pre>controllerProvider:
31678            *   function(MyResolveData) {
31679            *     if (MyResolveData.foo)
31680            *       return "FooCtrl"
31681            *     else if (MyResolveData.bar)
31682            *       return "BarCtrl";
31683            *     else return function($scope) {
31684            *       $scope.baz = "Qux";
31685            *     }
31686            *   }</pre>
31687            *
31688            * @param {string=} stateConfig.controllerAs
31689            * <a id='controllerAs'></a>
31690            * 
31691            * A controller alias name. If present the controller will be
31692            *   published to scope under the controllerAs name.
31693            * <pre>controllerAs: "myCtrl"</pre>
31694            *
31695            * @param {string|object=} stateConfig.parent
31696            * <a id='parent'></a>
31697            * Optionally specifies the parent state of this state.
31698            *
31699            * <pre>parent: 'parentState'</pre>
31700            * <pre>parent: parentState // JS variable</pre>
31701            *
31702            * @param {object=} stateConfig.resolve
31703            * <a id='resolve'></a>
31704            *
31705            * An optional map&lt;string, function&gt; of dependencies which
31706            *   should be injected into the controller. If any of these dependencies are promises, 
31707            *   the router will wait for them all to be resolved before the controller is instantiated.
31708            *   If all the promises are resolved successfully, the $stateChangeSuccess event is fired
31709            *   and the values of the resolved promises are injected into any controllers that reference them.
31710            *   If any  of the promises are rejected the $stateChangeError event is fired.
31711            *
31712            *   The map object is:
31713            *   
31714            *   - key - {string}: name of dependency to be injected into controller
31715            *   - factory - {string|function}: If string then it is alias for service. Otherwise if function, 
31716            *     it is injected and return value it treated as dependency. If result is a promise, it is 
31717            *     resolved before its value is injected into controller.
31718            *
31719            * <pre>resolve: {
31720            *     myResolve1:
31721            *       function($http, $stateParams) {
31722            *         return $http.get("/api/foos/"+stateParams.fooID);
31723            *       }
31724            *     }</pre>
31725            *
31726            * @param {string=} stateConfig.url
31727            * <a id='url'></a>
31728            *
31729            *   A url fragment with optional parameters. When a state is navigated or
31730            *   transitioned to, the `$stateParams` service will be populated with any 
31731            *   parameters that were passed.
31732            *
31733            *   (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
31734            *   more details on acceptable patterns )
31735            *
31736            * examples:
31737            * <pre>url: "/home"
31738            * url: "/users/:userid"
31739            * url: "/books/{bookid:[a-zA-Z_-]}"
31740            * url: "/books/{categoryid:int}"
31741            * url: "/books/{publishername:string}/{categoryid:int}"
31742            * url: "/messages?before&after"
31743            * url: "/messages?{before:date}&{after:date}"
31744            * url: "/messages/:mailboxid?{before:date}&{after:date}"
31745            * </pre>
31746            *
31747            * @param {object=} stateConfig.views
31748            * <a id='views'></a>
31749            * an optional map&lt;string, object&gt; which defined multiple views, or targets views
31750            * manually/explicitly.
31751            *
31752            * Examples:
31753            *
31754            * Targets three named `ui-view`s in the parent state's template
31755            * <pre>views: {
31756            *     header: {
31757            *       controller: "headerCtrl",
31758            *       templateUrl: "header.html"
31759            *     }, body: {
31760            *       controller: "bodyCtrl",
31761            *       templateUrl: "body.html"
31762            *     }, footer: {
31763            *       controller: "footCtrl",
31764            *       templateUrl: "footer.html"
31765            *     }
31766            *   }</pre>
31767            *
31768            * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
31769            * <pre>views: {
31770            *     'header@top': {
31771            *       controller: "msgHeaderCtrl",
31772            *       templateUrl: "msgHeader.html"
31773            *     }, 'body': {
31774            *       controller: "messagesCtrl",
31775            *       templateUrl: "messages.html"
31776            *     }
31777            *   }</pre>
31778            *
31779            * @param {boolean=} [stateConfig.abstract=false]
31780            * <a id='abstract'></a>
31781            * An abstract state will never be directly activated,
31782            *   but can provide inherited properties to its common children states.
31783            * <pre>abstract: true</pre>
31784            *
31785            * @param {function=} stateConfig.onEnter
31786            * <a id='onEnter'></a>
31787            *
31788            * Callback function for when a state is entered. Good way
31789            *   to trigger an action or dispatch an event, such as opening a dialog.
31790            * If minifying your scripts, make sure to explictly annotate this function,
31791            * because it won't be automatically annotated by your build tools.
31792            *
31793            * <pre>onEnter: function(MyService, $stateParams) {
31794            *     MyService.foo($stateParams.myParam);
31795            * }</pre>
31796            *
31797            * @param {function=} stateConfig.onExit
31798            * <a id='onExit'></a>
31799            *
31800            * Callback function for when a state is exited. Good way to
31801            *   trigger an action or dispatch an event, such as opening a dialog.
31802            * If minifying your scripts, make sure to explictly annotate this function,
31803            * because it won't be automatically annotated by your build tools.
31804            *
31805            * <pre>onExit: function(MyService, $stateParams) {
31806            *     MyService.cleanup($stateParams.myParam);
31807            * }</pre>
31808            *
31809            * @param {boolean=} [stateConfig.reloadOnSearch=true]
31810            * <a id='reloadOnSearch'></a>
31811            *
31812            * If `false`, will not retrigger the same state
31813            *   just because a search/query parameter has changed (via $location.search() or $location.hash()). 
31814            *   Useful for when you'd like to modify $location.search() without triggering a reload.
31815            * <pre>reloadOnSearch: false</pre>
31816            *
31817            * @param {object=} stateConfig.data
31818            * <a id='data'></a>
31819            *
31820            * Arbitrary data object, useful for custom configuration.  The parent state's `data` is
31821            *   prototypally inherited.  In other words, adding a data property to a state adds it to
31822            *   the entire subtree via prototypal inheritance.
31823            *
31824            * <pre>data: {
31825            *     requiredRole: 'foo'
31826            * } </pre>
31827            *
31828            * @param {object=} stateConfig.params
31829            * <a id='params'></a>
31830            *
31831            * A map which optionally configures parameters declared in the `url`, or
31832            *   defines additional non-url parameters.  For each parameter being
31833            *   configured, add a configuration object keyed to the name of the parameter.
31834            *
31835            *   Each parameter configuration object may contain the following properties:
31836            *
31837            *   - ** value ** - {object|function=}: specifies the default value for this
31838            *     parameter.  This implicitly sets this parameter as optional.
31839            *
31840            *     When UI-Router routes to a state and no value is
31841            *     specified for this parameter in the URL or transition, the
31842            *     default value will be used instead.  If `value` is a function,
31843            *     it will be injected and invoked, and the return value used.
31844            *
31845            *     *Note*: `undefined` is treated as "no default value" while `null`
31846            *     is treated as "the default value is `null`".
31847            *
31848            *     *Shorthand*: If you only need to configure the default value of the
31849            *     parameter, you may use a shorthand syntax.   In the **`params`**
31850            *     map, instead mapping the param name to a full parameter configuration
31851            *     object, simply set map it to the default parameter value, e.g.:
31852            *
31853            * <pre>// define a parameter's default value
31854            * params: {
31855            *     param1: { value: "defaultValue" }
31856            * }
31857            * // shorthand default values
31858            * params: {
31859            *     param1: "defaultValue",
31860            *     param2: "param2Default"
31861            * }</pre>
31862            *
31863            *   - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
31864            *     treated as an array of values.  If you specified a Type, the value will be
31865            *     treated as an array of the specified Type.  Note: query parameter values
31866            *     default to a special `"auto"` mode.
31867            *
31868            *     For query parameters in `"auto"` mode, if multiple  values for a single parameter
31869            *     are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
31870            *     are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`).  However, if
31871            *     only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
31872            *     value (e.g.: `{ foo: '1' }`).
31873            *
31874            * <pre>params: {
31875            *     param1: { array: true }
31876            * }</pre>
31877            *
31878            *   - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
31879            *     the current parameter value is the same as the default value. If `squash` is not set, it uses the
31880            *     configured default squash policy.
31881            *     (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
31882            *
31883            *   There are three squash settings:
31884            *
31885            *     - false: The parameter's default value is not squashed.  It is encoded and included in the URL
31886            *     - true: The parameter's default value is omitted from the URL.  If the parameter is preceeded and followed
31887            *       by slashes in the state's `url` declaration, then one of those slashes are omitted.
31888            *       This can allow for cleaner looking URLs.
31889            *     - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of  your choice.
31890            *
31891            * <pre>params: {
31892            *     param1: {
31893            *       value: "defaultId",
31894            *       squash: true
31895            * } }
31896            * // squash "defaultValue" to "~"
31897            * params: {
31898            *     param1: {
31899            *       value: "defaultValue",
31900            *       squash: "~"
31901            * } }
31902            * </pre>
31903            *
31904            *
31905            * @example
31906            * <pre>
31907            * // Some state name examples
31908            *
31909            * // stateName can be a single top-level name (must be unique).
31910            * $stateProvider.state("home", {});
31911            *
31912            * // Or it can be a nested state name. This state is a child of the
31913            * // above "home" state.
31914            * $stateProvider.state("home.newest", {});
31915            *
31916            * // Nest states as deeply as needed.
31917            * $stateProvider.state("home.newest.abc.xyz.inception", {});
31918            *
31919            * // state() returns $stateProvider, so you can chain state declarations.
31920            * $stateProvider
31921            *   .state("home", {})
31922            *   .state("about", {})
31923            *   .state("contacts", {});
31924            * </pre>
31925            *
31926            */
31927           this.state = state;
31928           function state(name, definition) {
31929             /*jshint validthis: true */
31930             if (isObject(name)) definition = name;
31931             else definition.name = name;
31932             registerState(definition);
31933             return this;
31934           }
31935
31936           /**
31937            * @ngdoc object
31938            * @name ui.router.state.$state
31939            *
31940            * @requires $rootScope
31941            * @requires $q
31942            * @requires ui.router.state.$view
31943            * @requires $injector
31944            * @requires ui.router.util.$resolve
31945            * @requires ui.router.state.$stateParams
31946            * @requires ui.router.router.$urlRouter
31947            *
31948            * @property {object} params A param object, e.g. {sectionId: section.id)}, that 
31949            * you'd like to test against the current active state.
31950            * @property {object} current A reference to the state's config object. However 
31951            * you passed it in. Useful for accessing custom data.
31952            * @property {object} transition Currently pending transition. A promise that'll 
31953            * resolve or reject.
31954            *
31955            * @description
31956            * `$state` service is responsible for representing states as well as transitioning
31957            * between them. It also provides interfaces to ask for current state or even states
31958            * you're coming from.
31959            */
31960           this.$get = $get;
31961           $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
31962           function $get(   $rootScope,   $q,   $view,   $injector,   $resolve,   $stateParams,   $urlRouter,   $location,   $urlMatcherFactory) {
31963
31964             var TransitionSuperseded = $q.reject(new Error('transition superseded'));
31965             var TransitionPrevented = $q.reject(new Error('transition prevented'));
31966             var TransitionAborted = $q.reject(new Error('transition aborted'));
31967             var TransitionFailed = $q.reject(new Error('transition failed'));
31968
31969             // Handles the case where a state which is the target of a transition is not found, and the user
31970             // can optionally retry or defer the transition
31971             function handleRedirect(redirect, state, params, options) {
31972               /**
31973                * @ngdoc event
31974                * @name ui.router.state.$state#$stateNotFound
31975                * @eventOf ui.router.state.$state
31976                * @eventType broadcast on root scope
31977                * @description
31978                * Fired when a requested state **cannot be found** using the provided state name during transition.
31979                * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
31980                * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
31981                * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
31982                * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
31983                *
31984                * @param {Object} event Event object.
31985                * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
31986                * @param {State} fromState Current state object.
31987                * @param {Object} fromParams Current state params.
31988                *
31989                * @example
31990                *
31991                * <pre>
31992                * // somewhere, assume lazy.state has not been defined
31993                * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
31994                *
31995                * // somewhere else
31996                * $scope.$on('$stateNotFound',
31997                * function(event, unfoundState, fromState, fromParams){
31998                *     console.log(unfoundState.to); // "lazy.state"
31999                *     console.log(unfoundState.toParams); // {a:1, b:2}
32000                *     console.log(unfoundState.options); // {inherit:false} + default options
32001                * })
32002                * </pre>
32003                */
32004               var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
32005
32006               if (evt.defaultPrevented) {
32007                 $urlRouter.update();
32008                 return TransitionAborted;
32009               }
32010
32011               if (!evt.retry) {
32012                 return null;
32013               }
32014
32015               // Allow the handler to return a promise to defer state lookup retry
32016               if (options.$retry) {
32017                 $urlRouter.update();
32018                 return TransitionFailed;
32019               }
32020               var retryTransition = $state.transition = $q.when(evt.retry);
32021
32022               retryTransition.then(function() {
32023                 if (retryTransition !== $state.transition) return TransitionSuperseded;
32024                 redirect.options.$retry = true;
32025                 return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
32026               }, function() {
32027                 return TransitionAborted;
32028               });
32029               $urlRouter.update();
32030
32031               return retryTransition;
32032             }
32033
32034             root.locals = { resolve: null, globals: { $stateParams: {} } };
32035
32036             $state = {
32037               params: {},
32038               current: root.self,
32039               $current: root,
32040               transition: null
32041             };
32042
32043             /**
32044              * @ngdoc function
32045              * @name ui.router.state.$state#reload
32046              * @methodOf ui.router.state.$state
32047              *
32048              * @description
32049              * A method that force reloads the current state. All resolves are re-resolved,
32050              * controllers reinstantiated, and events re-fired.
32051              *
32052              * @example
32053              * <pre>
32054              * var app angular.module('app', ['ui.router']);
32055              *
32056              * app.controller('ctrl', function ($scope, $state) {
32057              *   $scope.reload = function(){
32058              *     $state.reload();
32059              *   }
32060              * });
32061              * </pre>
32062              *
32063              * `reload()` is just an alias for:
32064              * <pre>
32065              * $state.transitionTo($state.current, $stateParams, { 
32066              *   reload: true, inherit: false, notify: true
32067              * });
32068              * </pre>
32069              *
32070              * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
32071              * @example
32072              * <pre>
32073              * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
32074              * //and current state is 'contacts.detail.item'
32075              * var app angular.module('app', ['ui.router']);
32076              *
32077              * app.controller('ctrl', function ($scope, $state) {
32078              *   $scope.reload = function(){
32079              *     //will reload 'contact.detail' and 'contact.detail.item' states
32080              *     $state.reload('contact.detail');
32081              *   }
32082              * });
32083              * </pre>
32084              *
32085              * `reload()` is just an alias for:
32086              * <pre>
32087              * $state.transitionTo($state.current, $stateParams, { 
32088              *   reload: true, inherit: false, notify: true
32089              * });
32090              * </pre>
32091
32092              * @returns {promise} A promise representing the state of the new transition. See
32093              * {@link ui.router.state.$state#methods_go $state.go}.
32094              */
32095             $state.reload = function reload(state) {
32096               return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
32097             };
32098
32099             /**
32100              * @ngdoc function
32101              * @name ui.router.state.$state#go
32102              * @methodOf ui.router.state.$state
32103              *
32104              * @description
32105              * Convenience method for transitioning to a new state. `$state.go` calls 
32106              * `$state.transitionTo` internally but automatically sets options to 
32107              * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. 
32108              * This allows you to easily use an absolute or relative to path and specify 
32109              * only the parameters you'd like to update (while letting unspecified parameters 
32110              * inherit from the currently active ancestor states).
32111              *
32112              * @example
32113              * <pre>
32114              * var app = angular.module('app', ['ui.router']);
32115              *
32116              * app.controller('ctrl', function ($scope, $state) {
32117              *   $scope.changeState = function () {
32118              *     $state.go('contact.detail');
32119              *   };
32120              * });
32121              * </pre>
32122              * <img src='../ngdoc_assets/StateGoExamples.png'/>
32123              *
32124              * @param {string} to Absolute state name or relative state path. Some examples:
32125              *
32126              * - `$state.go('contact.detail')` - will go to the `contact.detail` state
32127              * - `$state.go('^')` - will go to a parent state
32128              * - `$state.go('^.sibling')` - will go to a sibling state
32129              * - `$state.go('.child.grandchild')` - will go to grandchild state
32130              *
32131              * @param {object=} params A map of the parameters that will be sent to the state, 
32132              * will populate $stateParams. Any parameters that are not specified will be inherited from currently 
32133              * defined parameters. This allows, for example, going to a sibling state that shares parameters
32134              * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
32135              * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
32136              * will get you all current parameters, etc.
32137              * @param {object=} options Options object. The options are:
32138              *
32139              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32140              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32141              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32142              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32143              *    defines which state to be relative from.
32144              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32145              * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params 
32146              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32147              *    use this when you want to force a reload when *everything* is the same, including search params.
32148              *
32149              * @returns {promise} A promise representing the state of the new transition.
32150              *
32151              * Possible success values:
32152              *
32153              * - $state.current
32154              *
32155              * <br/>Possible rejection values:
32156              *
32157              * - 'transition superseded' - when a newer transition has been started after this one
32158              * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
32159              * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
32160              *   when a `$stateNotFound` `event.retry` promise errors.
32161              * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
32162              * - *resolve error* - when an error has occurred with a `resolve`
32163              *
32164              */
32165             $state.go = function go(to, params, options) {
32166               return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
32167             };
32168
32169             /**
32170              * @ngdoc function
32171              * @name ui.router.state.$state#transitionTo
32172              * @methodOf ui.router.state.$state
32173              *
32174              * @description
32175              * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
32176              * uses `transitionTo` internally. `$state.go` is recommended in most situations.
32177              *
32178              * @example
32179              * <pre>
32180              * var app = angular.module('app', ['ui.router']);
32181              *
32182              * app.controller('ctrl', function ($scope, $state) {
32183              *   $scope.changeState = function () {
32184              *     $state.transitionTo('contact.detail');
32185              *   };
32186              * });
32187              * </pre>
32188              *
32189              * @param {string} to State name.
32190              * @param {object=} toParams A map of the parameters that will be sent to the state,
32191              * will populate $stateParams.
32192              * @param {object=} options Options object. The options are:
32193              *
32194              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32195              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32196              * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
32197              * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), 
32198              *    defines which state to be relative from.
32199              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32200              * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params 
32201              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32202              *    use this when you want to force a reload when *everything* is the same, including search params.
32203              *    if String, then will reload the state with the name given in reload, and any children.
32204              *    if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
32205              *
32206              * @returns {promise} A promise representing the state of the new transition. See
32207              * {@link ui.router.state.$state#methods_go $state.go}.
32208              */
32209             $state.transitionTo = function transitionTo(to, toParams, options) {
32210               toParams = toParams || {};
32211               options = extend({
32212                 location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
32213               }, options || {});
32214
32215               var from = $state.$current, fromParams = $state.params, fromPath = from.path;
32216               var evt, toState = findState(to, options.relative);
32217
32218               // Store the hash param for later (since it will be stripped out by various methods)
32219               var hash = toParams['#'];
32220
32221               if (!isDefined(toState)) {
32222                 var redirect = { to: to, toParams: toParams, options: options };
32223                 var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
32224
32225                 if (redirectResult) {
32226                   return redirectResult;
32227                 }
32228
32229                 // Always retry once if the $stateNotFound was not prevented
32230                 // (handles either redirect changed or state lazy-definition)
32231                 to = redirect.to;
32232                 toParams = redirect.toParams;
32233                 options = redirect.options;
32234                 toState = findState(to, options.relative);
32235
32236                 if (!isDefined(toState)) {
32237                   if (!options.relative) throw new Error("No such state '" + to + "'");
32238                   throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
32239                 }
32240               }
32241               if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
32242               if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
32243               if (!toState.params.$$validates(toParams)) return TransitionFailed;
32244
32245               toParams = toState.params.$$values(toParams);
32246               to = toState;
32247
32248               var toPath = to.path;
32249
32250               // Starting from the root of the path, keep all levels that haven't changed
32251               var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
32252
32253               if (!options.reload) {
32254                 while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
32255                   locals = toLocals[keep] = state.locals;
32256                   keep++;
32257                   state = toPath[keep];
32258                 }
32259               } else if (isString(options.reload) || isObject(options.reload)) {
32260                 if (isObject(options.reload) && !options.reload.name) {
32261                   throw new Error('Invalid reload state object');
32262                 }
32263                 
32264                 var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
32265                 if (options.reload && !reloadState) {
32266                   throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
32267                 }
32268
32269                 while (state && state === fromPath[keep] && state !== reloadState) {
32270                   locals = toLocals[keep] = state.locals;
32271                   keep++;
32272                   state = toPath[keep];
32273                 }
32274               }
32275
32276               // If we're going to the same state and all locals are kept, we've got nothing to do.
32277               // But clear 'transition', as we still want to cancel any other pending transitions.
32278               // TODO: We may not want to bump 'transition' if we're called from a location change
32279               // that we've initiated ourselves, because we might accidentally abort a legitimate
32280               // transition initiated from code?
32281               if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
32282                 if (hash) toParams['#'] = hash;
32283                 $state.params = toParams;
32284                 copy($state.params, $stateParams);
32285                 if (options.location && to.navigable && to.navigable.url) {
32286                   $urlRouter.push(to.navigable.url, toParams, {
32287                     $$avoidResync: true, replace: options.location === 'replace'
32288                   });
32289                   $urlRouter.update(true);
32290                 }
32291                 $state.transition = null;
32292                 return $q.when($state.current);
32293               }
32294
32295               // Filter parameters before we pass them to event handlers etc.
32296               toParams = filterByKeys(to.params.$$keys(), toParams || {});
32297
32298               // Broadcast start event and cancel the transition if requested
32299               if (options.notify) {
32300                 /**
32301                  * @ngdoc event
32302                  * @name ui.router.state.$state#$stateChangeStart
32303                  * @eventOf ui.router.state.$state
32304                  * @eventType broadcast on root scope
32305                  * @description
32306                  * Fired when the state transition **begins**. You can use `event.preventDefault()`
32307                  * to prevent the transition from happening and then the transition promise will be
32308                  * rejected with a `'transition prevented'` value.
32309                  *
32310                  * @param {Object} event Event object.
32311                  * @param {State} toState The state being transitioned to.
32312                  * @param {Object} toParams The params supplied to the `toState`.
32313                  * @param {State} fromState The current state, pre-transition.
32314                  * @param {Object} fromParams The params supplied to the `fromState`.
32315                  *
32316                  * @example
32317                  *
32318                  * <pre>
32319                  * $rootScope.$on('$stateChangeStart',
32320                  * function(event, toState, toParams, fromState, fromParams){
32321                  *     event.preventDefault();
32322                  *     // transitionTo() promise will be rejected with
32323                  *     // a 'transition prevented' error
32324                  * })
32325                  * </pre>
32326                  */
32327                 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
32328                   $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32329                   $urlRouter.update();
32330                   return TransitionPrevented;
32331                 }
32332               }
32333
32334               // Resolve locals for the remaining states, but don't update any global state just
32335               // yet -- if anything fails to resolve the current state needs to remain untouched.
32336               // We also set up an inheritance chain for the locals here. This allows the view directive
32337               // to quickly look up the correct definition for each view in the current state. Even
32338               // though we create the locals object itself outside resolveState(), it is initially
32339               // empty and gets filled asynchronously. We need to keep track of the promise for the
32340               // (fully resolved) current locals, and pass this down the chain.
32341               var resolved = $q.when(locals);
32342
32343               for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
32344                 locals = toLocals[l] = inherit(locals);
32345                 resolved = resolveState(state, toParams, state === to, resolved, locals, options);
32346               }
32347
32348               // Once everything is resolved, we are ready to perform the actual transition
32349               // and return a promise for the new state. We also keep track of what the
32350               // current promise is, so that we can detect overlapping transitions and
32351               // keep only the outcome of the last transition.
32352               var transition = $state.transition = resolved.then(function () {
32353                 var l, entering, exiting;
32354
32355                 if ($state.transition !== transition) return TransitionSuperseded;
32356
32357                 // Exit 'from' states not kept
32358                 for (l = fromPath.length - 1; l >= keep; l--) {
32359                   exiting = fromPath[l];
32360                   if (exiting.self.onExit) {
32361                     $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
32362                   }
32363                   exiting.locals = null;
32364                 }
32365
32366                 // Enter 'to' states not kept
32367                 for (l = keep; l < toPath.length; l++) {
32368                   entering = toPath[l];
32369                   entering.locals = toLocals[l];
32370                   if (entering.self.onEnter) {
32371                     $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
32372                   }
32373                 }
32374
32375                 // Re-add the saved hash before we start returning things
32376                 if (hash) toParams['#'] = hash;
32377
32378                 // Run it again, to catch any transitions in callbacks
32379                 if ($state.transition !== transition) return TransitionSuperseded;
32380
32381                 // Update globals in $state
32382                 $state.$current = to;
32383                 $state.current = to.self;
32384                 $state.params = toParams;
32385                 copy($state.params, $stateParams);
32386                 $state.transition = null;
32387
32388                 if (options.location && to.navigable) {
32389                   $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
32390                     $$avoidResync: true, replace: options.location === 'replace'
32391                   });
32392                 }
32393
32394                 if (options.notify) {
32395                 /**
32396                  * @ngdoc event
32397                  * @name ui.router.state.$state#$stateChangeSuccess
32398                  * @eventOf ui.router.state.$state
32399                  * @eventType broadcast on root scope
32400                  * @description
32401                  * Fired once the state transition is **complete**.
32402                  *
32403                  * @param {Object} event Event object.
32404                  * @param {State} toState The state being transitioned to.
32405                  * @param {Object} toParams The params supplied to the `toState`.
32406                  * @param {State} fromState The current state, pre-transition.
32407                  * @param {Object} fromParams The params supplied to the `fromState`.
32408                  */
32409                   $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
32410                 }
32411                 $urlRouter.update(true);
32412
32413                 return $state.current;
32414               }, function (error) {
32415                 if ($state.transition !== transition) return TransitionSuperseded;
32416
32417                 $state.transition = null;
32418                 /**
32419                  * @ngdoc event
32420                  * @name ui.router.state.$state#$stateChangeError
32421                  * @eventOf ui.router.state.$state
32422                  * @eventType broadcast on root scope
32423                  * @description
32424                  * Fired when an **error occurs** during transition. It's important to note that if you
32425                  * have any errors in your resolve functions (javascript errors, non-existent services, etc)
32426                  * they will not throw traditionally. You must listen for this $stateChangeError event to
32427                  * catch **ALL** errors.
32428                  *
32429                  * @param {Object} event Event object.
32430                  * @param {State} toState The state being transitioned to.
32431                  * @param {Object} toParams The params supplied to the `toState`.
32432                  * @param {State} fromState The current state, pre-transition.
32433                  * @param {Object} fromParams The params supplied to the `fromState`.
32434                  * @param {Error} error The resolve error object.
32435                  */
32436                 evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
32437
32438                 if (!evt.defaultPrevented) {
32439                     $urlRouter.update();
32440                 }
32441
32442                 return $q.reject(error);
32443               });
32444
32445               return transition;
32446             };
32447
32448             /**
32449              * @ngdoc function
32450              * @name ui.router.state.$state#is
32451              * @methodOf ui.router.state.$state
32452              *
32453              * @description
32454              * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
32455              * but only checks for the full state name. If params is supplied then it will be
32456              * tested for strict equality against the current active params object, so all params
32457              * must match with none missing and no extras.
32458              *
32459              * @example
32460              * <pre>
32461              * $state.$current.name = 'contacts.details.item';
32462              *
32463              * // absolute name
32464              * $state.is('contact.details.item'); // returns true
32465              * $state.is(contactDetailItemStateObject); // returns true
32466              *
32467              * // relative name (. and ^), typically from a template
32468              * // E.g. from the 'contacts.details' template
32469              * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
32470              * </pre>
32471              *
32472              * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
32473              * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
32474              * to test against the current active state.
32475              * @param {object=} options An options object.  The options are:
32476              *
32477              * - **`relative`** - {string|object} -  If `stateOrName` is a relative state name and `options.relative` is set, .is will
32478              * test relative to `options.relative` state (or name).
32479              *
32480              * @returns {boolean} Returns true if it is the state.
32481              */
32482             $state.is = function is(stateOrName, params, options) {
32483               options = extend({ relative: $state.$current }, options || {});
32484               var state = findState(stateOrName, options.relative);
32485
32486               if (!isDefined(state)) { return undefined; }
32487               if ($state.$current !== state) { return false; }
32488               return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
32489             };
32490
32491             /**
32492              * @ngdoc function
32493              * @name ui.router.state.$state#includes
32494              * @methodOf ui.router.state.$state
32495              *
32496              * @description
32497              * A method to determine if the current active state is equal to or is the child of the
32498              * state stateName. If any params are passed then they will be tested for a match as well.
32499              * Not all the parameters need to be passed, just the ones you'd like to test for equality.
32500              *
32501              * @example
32502              * Partial and relative names
32503              * <pre>
32504              * $state.$current.name = 'contacts.details.item';
32505              *
32506              * // Using partial names
32507              * $state.includes("contacts"); // returns true
32508              * $state.includes("contacts.details"); // returns true
32509              * $state.includes("contacts.details.item"); // returns true
32510              * $state.includes("contacts.list"); // returns false
32511              * $state.includes("about"); // returns false
32512              *
32513              * // Using relative names (. and ^), typically from a template
32514              * // E.g. from the 'contacts.details' template
32515              * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
32516              * </pre>
32517              *
32518              * Basic globbing patterns
32519              * <pre>
32520              * $state.$current.name = 'contacts.details.item.url';
32521              *
32522              * $state.includes("*.details.*.*"); // returns true
32523              * $state.includes("*.details.**"); // returns true
32524              * $state.includes("**.item.**"); // returns true
32525              * $state.includes("*.details.item.url"); // returns true
32526              * $state.includes("*.details.*.url"); // returns true
32527              * $state.includes("*.details.*"); // returns false
32528              * $state.includes("item.**"); // returns false
32529              * </pre>
32530              *
32531              * @param {string} stateOrName A partial name, relative name, or glob pattern
32532              * to be searched for within the current state name.
32533              * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
32534              * that you'd like to test against the current active state.
32535              * @param {object=} options An options object.  The options are:
32536              *
32537              * - **`relative`** - {string|object=} -  If `stateOrName` is a relative state reference and `options.relative` is set,
32538              * .includes will test relative to `options.relative` state (or name).
32539              *
32540              * @returns {boolean} Returns true if it does include the state
32541              */
32542             $state.includes = function includes(stateOrName, params, options) {
32543               options = extend({ relative: $state.$current }, options || {});
32544               if (isString(stateOrName) && isGlob(stateOrName)) {
32545                 if (!doesStateMatchGlob(stateOrName)) {
32546                   return false;
32547                 }
32548                 stateOrName = $state.$current.name;
32549               }
32550
32551               var state = findState(stateOrName, options.relative);
32552               if (!isDefined(state)) { return undefined; }
32553               if (!isDefined($state.$current.includes[state.name])) { return false; }
32554               return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
32555             };
32556
32557
32558             /**
32559              * @ngdoc function
32560              * @name ui.router.state.$state#href
32561              * @methodOf ui.router.state.$state
32562              *
32563              * @description
32564              * A url generation method that returns the compiled url for the given state populated with the given params.
32565              *
32566              * @example
32567              * <pre>
32568              * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
32569              * </pre>
32570              *
32571              * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
32572              * @param {object=} params An object of parameter values to fill the state's required parameters.
32573              * @param {object=} options Options object. The options are:
32574              *
32575              * - **`lossy`** - {boolean=true} -  If true, and if there is no url associated with the state provided in the
32576              *    first parameter, then the constructed href url will be built from the first navigable ancestor (aka
32577              *    ancestor with a valid url).
32578              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32579              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32580              *    defines which state to be relative from.
32581              * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
32582              * 
32583              * @returns {string} compiled state url
32584              */
32585             $state.href = function href(stateOrName, params, options) {
32586               options = extend({
32587                 lossy:    true,
32588                 inherit:  true,
32589                 absolute: false,
32590                 relative: $state.$current
32591               }, options || {});
32592
32593               var state = findState(stateOrName, options.relative);
32594
32595               if (!isDefined(state)) return null;
32596               if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
32597               
32598               var nav = (state && options.lossy) ? state.navigable : state;
32599
32600               if (!nav || nav.url === undefined || nav.url === null) {
32601                 return null;
32602               }
32603               return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
32604                 absolute: options.absolute
32605               });
32606             };
32607
32608             /**
32609              * @ngdoc function
32610              * @name ui.router.state.$state#get
32611              * @methodOf ui.router.state.$state
32612              *
32613              * @description
32614              * Returns the state configuration object for any specific state or all states.
32615              *
32616              * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
32617              * the requested state. If not provided, returns an array of ALL state configs.
32618              * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
32619              * @returns {Object|Array} State configuration object or array of all objects.
32620              */
32621             $state.get = function (stateOrName, context) {
32622               if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
32623               var state = findState(stateOrName, context || $state.$current);
32624               return (state && state.self) ? state.self : null;
32625             };
32626
32627             function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
32628               // Make a restricted $stateParams with only the parameters that apply to this state if
32629               // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
32630               // we also need $stateParams to be available for any $injector calls we make during the
32631               // dependency resolution process.
32632               var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
32633               var locals = { $stateParams: $stateParams };
32634
32635               // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
32636               // We're also including $stateParams in this; that way the parameters are restricted
32637               // to the set that should be visible to the state, and are independent of when we update
32638               // the global $state and $stateParams values.
32639               dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
32640               var promises = [dst.resolve.then(function (globals) {
32641                 dst.globals = globals;
32642               })];
32643               if (inherited) promises.push(inherited);
32644
32645               function resolveViews() {
32646                 var viewsPromises = [];
32647
32648                 // Resolve template and dependencies for all views.
32649                 forEach(state.views, function (view, name) {
32650                   var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
32651                   injectables.$template = [ function () {
32652                     return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
32653                   }];
32654
32655                   viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
32656                     // References to the controller (only instantiated at link time)
32657                     if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
32658                       var injectLocals = angular.extend({}, injectables, dst.globals);
32659                       result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
32660                     } else {
32661                       result.$$controller = view.controller;
32662                     }
32663                     // Provide access to the state itself for internal use
32664                     result.$$state = state;
32665                     result.$$controllerAs = view.controllerAs;
32666                     dst[name] = result;
32667                   }));
32668                 });
32669
32670                 return $q.all(viewsPromises).then(function(){
32671                   return dst.globals;
32672                 });
32673               }
32674
32675               // Wait for all the promises and then return the activation object
32676               return $q.all(promises).then(resolveViews).then(function (values) {
32677                 return dst;
32678               });
32679             }
32680
32681             return $state;
32682           }
32683
32684           function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
32685             // Return true if there are no differences in non-search (path/object) params, false if there are differences
32686             function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
32687               // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
32688               function notSearchParam(key) {
32689                 return fromAndToState.params[key].location != "search";
32690               }
32691               var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
32692               var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
32693               var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
32694               return nonQueryParamSet.$$equals(fromParams, toParams);
32695             }
32696
32697             // If reload was not explicitly requested
32698             // and we're transitioning to the same state we're already in
32699             // and    the locals didn't change
32700             //     or they changed in a way that doesn't merit reloading
32701             //        (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
32702             // Then return true.
32703             if (!options.reload && to === from &&
32704               (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
32705               return true;
32706             }
32707           }
32708         }
32709
32710         angular.module('ui.router.state')
32711           .value('$stateParams', {})
32712           .provider('$state', $StateProvider);
32713
32714
32715         $ViewProvider.$inject = [];
32716         function $ViewProvider() {
32717
32718           this.$get = $get;
32719           /**
32720            * @ngdoc object
32721            * @name ui.router.state.$view
32722            *
32723            * @requires ui.router.util.$templateFactory
32724            * @requires $rootScope
32725            *
32726            * @description
32727            *
32728            */
32729           $get.$inject = ['$rootScope', '$templateFactory'];
32730           function $get(   $rootScope,   $templateFactory) {
32731             return {
32732               // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
32733               /**
32734                * @ngdoc function
32735                * @name ui.router.state.$view#load
32736                * @methodOf ui.router.state.$view
32737                *
32738                * @description
32739                *
32740                * @param {string} name name
32741                * @param {object} options option object.
32742                */
32743               load: function load(name, options) {
32744                 var result, defaults = {
32745                   template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
32746                 };
32747                 options = extend(defaults, options);
32748
32749                 if (options.view) {
32750                   result = $templateFactory.fromConfig(options.view, options.params, options.locals);
32751                 }
32752                 if (result && options.notify) {
32753                 /**
32754                  * @ngdoc event
32755                  * @name ui.router.state.$state#$viewContentLoading
32756                  * @eventOf ui.router.state.$view
32757                  * @eventType broadcast on root scope
32758                  * @description
32759                  *
32760                  * Fired once the view **begins loading**, *before* the DOM is rendered.
32761                  *
32762                  * @param {Object} event Event object.
32763                  * @param {Object} viewConfig The view config properties (template, controller, etc).
32764                  *
32765                  * @example
32766                  *
32767                  * <pre>
32768                  * $scope.$on('$viewContentLoading',
32769                  * function(event, viewConfig){
32770                  *     // Access to all the view config properties.
32771                  *     // and one special property 'targetView'
32772                  *     // viewConfig.targetView
32773                  * });
32774                  * </pre>
32775                  */
32776                   $rootScope.$broadcast('$viewContentLoading', options);
32777                 }
32778                 return result;
32779               }
32780             };
32781           }
32782         }
32783
32784         angular.module('ui.router.state').provider('$view', $ViewProvider);
32785
32786         /**
32787          * @ngdoc object
32788          * @name ui.router.state.$uiViewScrollProvider
32789          *
32790          * @description
32791          * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
32792          */
32793         function $ViewScrollProvider() {
32794
32795           var useAnchorScroll = false;
32796
32797           /**
32798            * @ngdoc function
32799            * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
32800            * @methodOf ui.router.state.$uiViewScrollProvider
32801            *
32802            * @description
32803            * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
32804            * scrolling based on the url anchor.
32805            */
32806           this.useAnchorScroll = function () {
32807             useAnchorScroll = true;
32808           };
32809
32810           /**
32811            * @ngdoc object
32812            * @name ui.router.state.$uiViewScroll
32813            *
32814            * @requires $anchorScroll
32815            * @requires $timeout
32816            *
32817            * @description
32818            * When called with a jqLite element, it scrolls the element into view (after a
32819            * `$timeout` so the DOM has time to refresh).
32820            *
32821            * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
32822            * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
32823            */
32824           this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
32825             if (useAnchorScroll) {
32826               return $anchorScroll;
32827             }
32828
32829             return function ($element) {
32830               return $timeout(function () {
32831                 $element[0].scrollIntoView();
32832               }, 0, false);
32833             };
32834           }];
32835         }
32836
32837         angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
32838
32839         /**
32840          * @ngdoc directive
32841          * @name ui.router.state.directive:ui-view
32842          *
32843          * @requires ui.router.state.$state
32844          * @requires $compile
32845          * @requires $controller
32846          * @requires $injector
32847          * @requires ui.router.state.$uiViewScroll
32848          * @requires $document
32849          *
32850          * @restrict ECA
32851          *
32852          * @description
32853          * The ui-view directive tells $state where to place your templates.
32854          *
32855          * @param {string=} name A view name. The name should be unique amongst the other views in the
32856          * same state. You can have views of the same name that live in different states.
32857          *
32858          * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
32859          * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
32860          * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
32861          * scroll ui-view elements into view when they are populated during a state activation.
32862          *
32863          * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
32864          * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
32865          *
32866          * @param {string=} onload Expression to evaluate whenever the view updates.
32867          * 
32868          * @example
32869          * A view can be unnamed or named. 
32870          * <pre>
32871          * <!-- Unnamed -->
32872          * <div ui-view></div> 
32873          * 
32874          * <!-- Named -->
32875          * <div ui-view="viewName"></div>
32876          * </pre>
32877          *
32878          * You can only have one unnamed view within any template (or root html). If you are only using a 
32879          * single view and it is unnamed then you can populate it like so:
32880          * <pre>
32881          * <div ui-view></div> 
32882          * $stateProvider.state("home", {
32883          *   template: "<h1>HELLO!</h1>"
32884          * })
32885          * </pre>
32886          * 
32887          * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
32888          * config property, by name, in this case an empty name:
32889          * <pre>
32890          * $stateProvider.state("home", {
32891          *   views: {
32892          *     "": {
32893          *       template: "<h1>HELLO!</h1>"
32894          *     }
32895          *   }    
32896          * })
32897          * </pre>
32898          * 
32899          * But typically you'll only use the views property if you name your view or have more than one view 
32900          * in the same template. There's not really a compelling reason to name a view if its the only one, 
32901          * but you could if you wanted, like so:
32902          * <pre>
32903          * <div ui-view="main"></div>
32904          * </pre> 
32905          * <pre>
32906          * $stateProvider.state("home", {
32907          *   views: {
32908          *     "main": {
32909          *       template: "<h1>HELLO!</h1>"
32910          *     }
32911          *   }    
32912          * })
32913          * </pre>
32914          * 
32915          * Really though, you'll use views to set up multiple views:
32916          * <pre>
32917          * <div ui-view></div>
32918          * <div ui-view="chart"></div> 
32919          * <div ui-view="data"></div> 
32920          * </pre>
32921          * 
32922          * <pre>
32923          * $stateProvider.state("home", {
32924          *   views: {
32925          *     "": {
32926          *       template: "<h1>HELLO!</h1>"
32927          *     },
32928          *     "chart": {
32929          *       template: "<chart_thing/>"
32930          *     },
32931          *     "data": {
32932          *       template: "<data_thing/>"
32933          *     }
32934          *   }    
32935          * })
32936          * </pre>
32937          *
32938          * Examples for `autoscroll`:
32939          *
32940          * <pre>
32941          * <!-- If autoscroll present with no expression,
32942          *      then scroll ui-view into view -->
32943          * <ui-view autoscroll/>
32944          *
32945          * <!-- If autoscroll present with valid expression,
32946          *      then scroll ui-view into view if expression evaluates to true -->
32947          * <ui-view autoscroll='true'/>
32948          * <ui-view autoscroll='false'/>
32949          * <ui-view autoscroll='scopeVariable'/>
32950          * </pre>
32951          */
32952         $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
32953         function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {
32954
32955           function getService() {
32956             return ($injector.has) ? function(service) {
32957               return $injector.has(service) ? $injector.get(service) : null;
32958             } : function(service) {
32959               try {
32960                 return $injector.get(service);
32961               } catch (e) {
32962                 return null;
32963               }
32964             };
32965           }
32966
32967           var service = getService(),
32968               $animator = service('$animator'),
32969               $animate = service('$animate');
32970
32971           // Returns a set of DOM manipulation functions based on which Angular version
32972           // it should use
32973           function getRenderer(attrs, scope) {
32974             var statics = function() {
32975               return {
32976                 enter: function (element, target, cb) { target.after(element); cb(); },
32977                 leave: function (element, cb) { element.remove(); cb(); }
32978               };
32979             };
32980
32981             if ($animate) {
32982               return {
32983                 enter: function(element, target, cb) {
32984                   var promise = $animate.enter(element, null, target, cb);
32985                   if (promise && promise.then) promise.then(cb);
32986                 },
32987                 leave: function(element, cb) {
32988                   var promise = $animate.leave(element, cb);
32989                   if (promise && promise.then) promise.then(cb);
32990                 }
32991               };
32992             }
32993
32994             if ($animator) {
32995               var animate = $animator && $animator(scope, attrs);
32996
32997               return {
32998                 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
32999                 leave: function(element, cb) { animate.leave(element); cb(); }
33000               };
33001             }
33002
33003             return statics();
33004           }
33005
33006           var directive = {
33007             restrict: 'ECA',
33008             terminal: true,
33009             priority: 400,
33010             transclude: 'element',
33011             compile: function (tElement, tAttrs, $transclude) {
33012               return function (scope, $element, attrs) {
33013                 var previousEl, currentEl, currentScope, latestLocals,
33014                     onloadExp     = attrs.onload || '',
33015                     autoScrollExp = attrs.autoscroll,
33016                     renderer      = getRenderer(attrs, scope);
33017
33018                 scope.$on('$stateChangeSuccess', function() {
33019                   updateView(false);
33020                 });
33021                 scope.$on('$viewContentLoading', function() {
33022                   updateView(false);
33023                 });
33024
33025                 updateView(true);
33026
33027                 function cleanupLastView() {
33028                   if (previousEl) {
33029                     previousEl.remove();
33030                     previousEl = null;
33031                   }
33032
33033                   if (currentScope) {
33034                     currentScope.$destroy();
33035                     currentScope = null;
33036                   }
33037
33038                   if (currentEl) {
33039                     renderer.leave(currentEl, function() {
33040                       previousEl = null;
33041                     });
33042
33043                     previousEl = currentEl;
33044                     currentEl = null;
33045                   }
33046                 }
33047
33048                 function updateView(firstTime) {
33049                   var newScope,
33050                       name            = getUiViewName(scope, attrs, $element, $interpolate),
33051                       previousLocals  = name && $state.$current && $state.$current.locals[name];
33052
33053                   if (!firstTime && previousLocals === latestLocals) return; // nothing to do
33054                   newScope = scope.$new();
33055                   latestLocals = $state.$current.locals[name];
33056
33057                   var clone = $transclude(newScope, function(clone) {
33058                     renderer.enter(clone, $element, function onUiViewEnter() {
33059                       if(currentScope) {
33060                         currentScope.$emit('$viewContentAnimationEnded');
33061                       }
33062
33063                       if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
33064                         $uiViewScroll(clone);
33065                       }
33066                     });
33067                     cleanupLastView();
33068                   });
33069
33070                   currentEl = clone;
33071                   currentScope = newScope;
33072                   /**
33073                    * @ngdoc event
33074                    * @name ui.router.state.directive:ui-view#$viewContentLoaded
33075                    * @eventOf ui.router.state.directive:ui-view
33076                    * @eventType emits on ui-view directive scope
33077                    * @description           *
33078                    * Fired once the view is **loaded**, *after* the DOM is rendered.
33079                    *
33080                    * @param {Object} event Event object.
33081                    */
33082                   currentScope.$emit('$viewContentLoaded');
33083                   currentScope.$eval(onloadExp);
33084                 }
33085               };
33086             }
33087           };
33088
33089           return directive;
33090         }
33091
33092         $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
33093         function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
33094           return {
33095             restrict: 'ECA',
33096             priority: -400,
33097             compile: function (tElement) {
33098               var initial = tElement.html();
33099               return function (scope, $element, attrs) {
33100                 var current = $state.$current,
33101                     name = getUiViewName(scope, attrs, $element, $interpolate),
33102                     locals  = current && current.locals[name];
33103
33104                 if (! locals) {
33105                   return;
33106                 }
33107
33108                 $element.data('$uiView', { name: name, state: locals.$$state });
33109                 $element.html(locals.$template ? locals.$template : initial);
33110
33111                 var link = $compile($element.contents());
33112
33113                 if (locals.$$controller) {
33114                   locals.$scope = scope;
33115                   locals.$element = $element;
33116                   var controller = $controller(locals.$$controller, locals);
33117                   if (locals.$$controllerAs) {
33118                     scope[locals.$$controllerAs] = controller;
33119                   }
33120                   $element.data('$ngControllerController', controller);
33121                   $element.children().data('$ngControllerController', controller);
33122                 }
33123
33124                 link(scope);
33125               };
33126             }
33127           };
33128         }
33129
33130         /**
33131          * Shared ui-view code for both directives:
33132          * Given scope, element, and its attributes, return the view's name
33133          */
33134         function getUiViewName(scope, attrs, element, $interpolate) {
33135           var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
33136           var inherited = element.inheritedData('$uiView');
33137           return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
33138         }
33139
33140         angular.module('ui.router.state').directive('uiView', $ViewDirective);
33141         angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
33142
33143         function parseStateRef(ref, current) {
33144           var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
33145           if (preparsed) ref = current + '(' + preparsed[1] + ')';
33146           parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
33147           if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
33148           return { state: parsed[1], paramExpr: parsed[3] || null };
33149         }
33150
33151         function stateContext(el) {
33152           var stateData = el.parent().inheritedData('$uiView');
33153
33154           if (stateData && stateData.state && stateData.state.name) {
33155             return stateData.state;
33156           }
33157         }
33158
33159         /**
33160          * @ngdoc directive
33161          * @name ui.router.state.directive:ui-sref
33162          *
33163          * @requires ui.router.state.$state
33164          * @requires $timeout
33165          *
33166          * @restrict A
33167          *
33168          * @description
33169          * A directive that binds a link (`<a>` tag) to a state. If the state has an associated 
33170          * URL, the directive will automatically generate & update the `href` attribute via 
33171          * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 
33172          * the link will trigger a state transition with optional parameters. 
33173          *
33174          * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 
33175          * handled natively by the browser.
33176          *
33177          * You can also use relative state paths within ui-sref, just like the relative 
33178          * paths passed to `$state.go()`. You just need to be aware that the path is relative
33179          * to the state that the link lives in, in other words the state that loaded the 
33180          * template containing the link.
33181          *
33182          * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
33183          * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
33184          * and `reload`.
33185          *
33186          * @example
33187          * Here's an example of how you'd use ui-sref and how it would compile. If you have the 
33188          * following template:
33189          * <pre>
33190          * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33191          * 
33192          * <ul>
33193          *     <li ng-repeat="contact in contacts">
33194          *         <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
33195          *     </li>
33196          * </ul>
33197          * </pre>
33198          * 
33199          * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33200          * <pre>
33201          * <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>
33202          * 
33203          * <ul>
33204          *     <li ng-repeat="contact in contacts">
33205          *         <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
33206          *     </li>
33207          *     <li ng-repeat="contact in contacts">
33208          *         <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
33209          *     </li>
33210          *     <li ng-repeat="contact in contacts">
33211          *         <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
33212          *     </li>
33213          * </ul>
33214          *
33215          * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
33216          * </pre>
33217          *
33218          * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
33219          * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33220          */
33221         $StateRefDirective.$inject = ['$state', '$timeout'];
33222         function $StateRefDirective($state, $timeout) {
33223           var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
33224
33225           return {
33226             restrict: 'A',
33227             require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33228             link: function(scope, element, attrs, uiSrefActive) {
33229               var ref = parseStateRef(attrs.uiSref, $state.current.name);
33230               var params = null, url = null, base = stateContext(element) || $state.$current;
33231               // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33232               var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
33233                          'xlink:href' : 'href';
33234               var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
33235               var isForm = element[0].nodeName === "FORM";
33236               var attr = isForm ? "action" : hrefKind, nav = true;
33237
33238               var options = { relative: base, inherit: true };
33239               var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
33240
33241               angular.forEach(allowedOptions, function(option) {
33242                 if (option in optionsOverride) {
33243                   options[option] = optionsOverride[option];
33244                 }
33245               });
33246
33247               var update = function(newVal) {
33248                 if (newVal) params = angular.copy(newVal);
33249                 if (!nav) return;
33250
33251                 newHref = $state.href(ref.state, params, options);
33252
33253                 var activeDirective = uiSrefActive[1] || uiSrefActive[0];
33254                 if (activeDirective) {
33255                   activeDirective.$$addStateInfo(ref.state, params);
33256                 }
33257                 if (newHref === null) {
33258                   nav = false;
33259                   return false;
33260                 }
33261                 attrs.$set(attr, newHref);
33262               };
33263
33264               if (ref.paramExpr) {
33265                 scope.$watch(ref.paramExpr, function(newVal, oldVal) {
33266                   if (newVal !== params) update(newVal);
33267                 }, true);
33268                 params = angular.copy(scope.$eval(ref.paramExpr));
33269               }
33270               update();
33271
33272               if (isForm) return;
33273
33274               element.bind("click", function(e) {
33275                 var button = e.which || e.button;
33276                 if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
33277                   // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33278                   var transition = $timeout(function() {
33279                     $state.go(ref.state, params, options);
33280                   });
33281                   e.preventDefault();
33282
33283                   // if the state has no URL, ignore one preventDefault from the <a> directive.
33284                   var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
33285                   e.preventDefault = function() {
33286                     if (ignorePreventDefaultCount-- <= 0)
33287                       $timeout.cancel(transition);
33288                   };
33289                 }
33290               });
33291             }
33292           };
33293         }
33294
33295         /**
33296          * @ngdoc directive
33297          * @name ui.router.state.directive:ui-sref-active
33298          *
33299          * @requires ui.router.state.$state
33300          * @requires ui.router.state.$stateParams
33301          * @requires $interpolate
33302          *
33303          * @restrict A
33304          *
33305          * @description
33306          * A directive working alongside ui-sref to add classes to an element when the
33307          * related ui-sref directive's state is active, and removing them when it is inactive.
33308          * The primary use-case is to simplify the special appearance of navigation menus
33309          * relying on `ui-sref`, by having the "active" state's menu button appear different,
33310          * distinguishing it from the inactive menu items.
33311          *
33312          * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
33313          * ui-sref-active found at the same level or above the ui-sref will be used.
33314          *
33315          * Will activate when the ui-sref's target state or any child state is active. If you
33316          * need to activate only when the ui-sref target state is active and *not* any of
33317          * it's children, then you will use
33318          * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
33319          *
33320          * @example
33321          * Given the following template:
33322          * <pre>
33323          * <ul>
33324          *   <li ui-sref-active="active" class="item">
33325          *     <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
33326          *   </li>
33327          * </ul>
33328          * </pre>
33329          *
33330          *
33331          * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
33332          * the resulting HTML will appear as (note the 'active' class):
33333          * <pre>
33334          * <ul>
33335          *   <li ui-sref-active="active" class="item active">
33336          *     <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
33337          *   </li>
33338          * </ul>
33339          * </pre>
33340          *
33341          * The class name is interpolated **once** during the directives link time (any further changes to the
33342          * interpolated value are ignored).
33343          *
33344          * Multiple classes may be specified in a space-separated format:
33345          * <pre>
33346          * <ul>
33347          *   <li ui-sref-active='class1 class2 class3'>
33348          *     <a ui-sref="app.user">link</a>
33349          *   </li>
33350          * </ul>
33351          * </pre>
33352          */
33353
33354         /**
33355          * @ngdoc directive
33356          * @name ui.router.state.directive:ui-sref-active-eq
33357          *
33358          * @requires ui.router.state.$state
33359          * @requires ui.router.state.$stateParams
33360          * @requires $interpolate
33361          *
33362          * @restrict A
33363          *
33364          * @description
33365          * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
33366          * when the exact target state used in the `ui-sref` is active; no child states.
33367          *
33368          */
33369         $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
33370         function $StateRefActiveDirective($state, $stateParams, $interpolate) {
33371           return  {
33372             restrict: "A",
33373             controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
33374               var states = [], activeClass;
33375
33376               // There probably isn't much point in $observing this
33377               // uiSrefActive and uiSrefActiveEq share the same directive object with some
33378               // slight difference in logic routing
33379               activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
33380
33381               // Allow uiSref to communicate with uiSrefActive[Equals]
33382               this.$$addStateInfo = function (newState, newParams) {
33383                 var state = $state.get(newState, stateContext($element));
33384
33385                 states.push({
33386                   state: state || { name: newState },
33387                   params: newParams
33388                 });
33389
33390                 update();
33391               };
33392
33393               $scope.$on('$stateChangeSuccess', update);
33394
33395               // Update route state
33396               function update() {
33397                 if (anyMatch()) {
33398                   $element.addClass(activeClass);
33399                 } else {
33400                   $element.removeClass(activeClass);
33401                 }
33402               }
33403
33404               function anyMatch() {
33405                 for (var i = 0; i < states.length; i++) {
33406                   if (isMatch(states[i].state, states[i].params)) {
33407                     return true;
33408                   }
33409                 }
33410                 return false;
33411               }
33412
33413               function isMatch(state, params) {
33414                 if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
33415                   return $state.is(state.name, params);
33416                 } else {
33417                   return $state.includes(state.name, params);
33418                 }
33419               }
33420             }]
33421           };
33422         }
33423
33424         angular.module('ui.router.state')
33425           .directive('uiSref', $StateRefDirective)
33426           .directive('uiSrefActive', $StateRefActiveDirective)
33427           .directive('uiSrefActiveEq', $StateRefActiveDirective);
33428
33429         /**
33430          * @ngdoc filter
33431          * @name ui.router.state.filter:isState
33432          *
33433          * @requires ui.router.state.$state
33434          *
33435          * @description
33436          * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
33437          */
33438         $IsStateFilter.$inject = ['$state'];
33439         function $IsStateFilter($state) {
33440           var isFilter = function (state) {
33441             return $state.is(state);
33442           };
33443           isFilter.$stateful = true;
33444           return isFilter;
33445         }
33446
33447         /**
33448          * @ngdoc filter
33449          * @name ui.router.state.filter:includedByState
33450          *
33451          * @requires ui.router.state.$state
33452          *
33453          * @description
33454          * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
33455          */
33456         $IncludedByStateFilter.$inject = ['$state'];
33457         function $IncludedByStateFilter($state) {
33458           var includesFilter = function (state) {
33459             return $state.includes(state);
33460           };
33461           includesFilter.$stateful = true;
33462           return  includesFilter;
33463         }
33464
33465         angular.module('ui.router.state')
33466           .filter('isState', $IsStateFilter)
33467           .filter('includedByState', $IncludedByStateFilter);
33468         })(window, window.angular);
33469
33470 /***/ },
33471 /* 4 */
33472 /***/ function(module, exports, __webpack_require__) {
33473
33474         __webpack_require__(5);
33475         module.exports = 'ngResource';
33476
33477
33478 /***/ },
33479 /* 5 */
33480 /***/ function(module, exports) {
33481
33482         /**
33483          * @license AngularJS v1.4.8
33484          * (c) 2010-2015 Google, Inc. http://angularjs.org
33485          * License: MIT
33486          */
33487         (function(window, angular, undefined) {'use strict';
33488
33489         var $resourceMinErr = angular.$$minErr('$resource');
33490
33491         // Helper functions and regex to lookup a dotted path on an object
33492         // stopping at undefined/null.  The path must be composed of ASCII
33493         // identifiers (just like $parse)
33494         var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
33495
33496         function isValidDottedPath(path) {
33497           return (path != null && path !== '' && path !== 'hasOwnProperty' &&
33498               MEMBER_NAME_REGEX.test('.' + path));
33499         }
33500
33501         function lookupDottedPath(obj, path) {
33502           if (!isValidDottedPath(path)) {
33503             throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
33504           }
33505           var keys = path.split('.');
33506           for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
33507             var key = keys[i];
33508             obj = (obj !== null) ? obj[key] : undefined;
33509           }
33510           return obj;
33511         }
33512
33513         /**
33514          * Create a shallow copy of an object and clear other fields from the destination
33515          */
33516         function shallowClearAndCopy(src, dst) {
33517           dst = dst || {};
33518
33519           angular.forEach(dst, function(value, key) {
33520             delete dst[key];
33521           });
33522
33523           for (var key in src) {
33524             if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
33525               dst[key] = src[key];
33526             }
33527           }
33528
33529           return dst;
33530         }
33531
33532         /**
33533          * @ngdoc module
33534          * @name ngResource
33535          * @description
33536          *
33537          * # ngResource
33538          *
33539          * The `ngResource` module provides interaction support with RESTful services
33540          * via the $resource service.
33541          *
33542          *
33543          * <div doc-module-components="ngResource"></div>
33544          *
33545          * See {@link ngResource.$resource `$resource`} for usage.
33546          */
33547
33548         /**
33549          * @ngdoc service
33550          * @name $resource
33551          * @requires $http
33552          *
33553          * @description
33554          * A factory which creates a resource object that lets you interact with
33555          * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
33556          *
33557          * The returned resource object has action methods which provide high-level behaviors without
33558          * the need to interact with the low level {@link ng.$http $http} service.
33559          *
33560          * Requires the {@link ngResource `ngResource`} module to be installed.
33561          *
33562          * By default, trailing slashes will be stripped from the calculated URLs,
33563          * which can pose problems with server backends that do not expect that
33564          * behavior.  This can be disabled by configuring the `$resourceProvider` like
33565          * this:
33566          *
33567          * ```js
33568              app.config(['$resourceProvider', function($resourceProvider) {
33569                // Don't strip trailing slashes from calculated URLs
33570                $resourceProvider.defaults.stripTrailingSlashes = false;
33571              }]);
33572          * ```
33573          *
33574          * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
33575          *   `/user/:username`. If you are using a URL with a port number (e.g.
33576          *   `http://example.com:8080/api`), it will be respected.
33577          *
33578          *   If you are using a url with a suffix, just add the suffix, like this:
33579          *   `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
33580          *   or even `$resource('http://example.com/resource/:resource_id.:format')`
33581          *   If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
33582          *   collapsed down to a single `.`.  If you need this sequence to appear and not collapse then you
33583          *   can escape it with `/\.`.
33584          *
33585          * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
33586          *   `actions` methods. If any of the parameter value is a function, it will be executed every time
33587          *   when a param value needs to be obtained for a request (unless the param was overridden).
33588          *
33589          *   Each key value in the parameter object is first bound to url template if present and then any
33590          *   excess keys are appended to the url search query after the `?`.
33591          *
33592          *   Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
33593          *   URL `/path/greet?salutation=Hello`.
33594          *
33595          *   If the parameter value is prefixed with `@` then the value for that parameter will be extracted
33596          *   from the corresponding property on the `data` object (provided when calling an action method).  For
33597          *   example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
33598          *   will be `data.someProp`.
33599          *
33600          * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
33601          *   the default set of resource actions. The declaration should be created in the format of {@link
33602          *   ng.$http#usage $http.config}:
33603          *
33604          *       {action1: {method:?, params:?, isArray:?, headers:?, ...},
33605          *        action2: {method:?, params:?, isArray:?, headers:?, ...},
33606          *        ...}
33607          *
33608          *   Where:
33609          *
33610          *   - **`action`** – {string} – The name of action. This name becomes the name of the method on
33611          *     your resource object.
33612          *   - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
33613          *     `DELETE`, `JSONP`, etc).
33614          *   - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
33615          *     the parameter value is a function, it will be executed every time when a param value needs to
33616          *     be obtained for a request (unless the param was overridden).
33617          *   - **`url`** – {string} – action specific `url` override. The url templating is supported just
33618          *     like for the resource-level urls.
33619          *   - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
33620          *     see `returns` section.
33621          *   - **`transformRequest`** –
33622          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33623          *     transform function or an array of such functions. The transform function takes the http
33624          *     request body and headers and returns its transformed (typically serialized) version.
33625          *     By default, transformRequest will contain one function that checks if the request data is
33626          *     an object and serializes to using `angular.toJson`. To prevent this behavior, set
33627          *     `transformRequest` to an empty array: `transformRequest: []`
33628          *   - **`transformResponse`** –
33629          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33630          *     transform function or an array of such functions. The transform function takes the http
33631          *     response body and headers and returns its transformed (typically deserialized) version.
33632          *     By default, transformResponse will contain one function that checks if the response looks like
33633          *     a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
33634          *     `transformResponse` to an empty array: `transformResponse: []`
33635          *   - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
33636          *     GET request, otherwise if a cache instance built with
33637          *     {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
33638          *     caching.
33639          *   - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
33640          *     should abort the request when resolved.
33641          *   - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
33642          *     XHR object. See
33643          *     [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
33644          *     for more information.
33645          *   - **`responseType`** - `{string}` - see
33646          *     [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
33647          *   - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
33648          *     `response` and `responseError`. Both `response` and `responseError` interceptors get called
33649          *     with `http response` object. See {@link ng.$http $http interceptors}.
33650          *
33651          * @param {Object} options Hash with custom settings that should extend the
33652          *   default `$resourceProvider` behavior.  The only supported option is
33653          *
33654          *   Where:
33655          *
33656          *   - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
33657          *   slashes from any calculated URL will be stripped. (Defaults to true.)
33658          *
33659          * @returns {Object} A resource "class" object with methods for the default set of resource actions
33660          *   optionally extended with custom `actions`. The default set contains these actions:
33661          *   ```js
33662          *   { 'get':    {method:'GET'},
33663          *     'save':   {method:'POST'},
33664          *     'query':  {method:'GET', isArray:true},
33665          *     'remove': {method:'DELETE'},
33666          *     'delete': {method:'DELETE'} };
33667          *   ```
33668          *
33669          *   Calling these methods invoke an {@link ng.$http} with the specified http method,
33670          *   destination and parameters. When the data is returned from the server then the object is an
33671          *   instance of the resource class. The actions `save`, `remove` and `delete` are available on it
33672          *   as  methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
33673          *   read, update, delete) on server-side data like this:
33674          *   ```js
33675          *   var User = $resource('/user/:userId', {userId:'@id'});
33676          *   var user = User.get({userId:123}, function() {
33677          *     user.abc = true;
33678          *     user.$save();
33679          *   });
33680          *   ```
33681          *
33682          *   It is important to realize that invoking a $resource object method immediately returns an
33683          *   empty reference (object or array depending on `isArray`). Once the data is returned from the
33684          *   server the existing reference is populated with the actual data. This is a useful trick since
33685          *   usually the resource is assigned to a model which is then rendered by the view. Having an empty
33686          *   object results in no rendering, once the data arrives from the server then the object is
33687          *   populated with the data and the view automatically re-renders itself showing the new data. This
33688          *   means that in most cases one never has to write a callback function for the action methods.
33689          *
33690          *   The action methods on the class object or instance object can be invoked with the following
33691          *   parameters:
33692          *
33693          *   - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
33694          *   - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
33695          *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
33696          *
33697          *
33698          *   Success callback is called with (value, responseHeaders) arguments, where the value is
33699          *   the populated resource instance or collection object. The error callback is called
33700          *   with (httpResponse) argument.
33701          *
33702          *   Class actions return empty instance (with additional properties below).
33703          *   Instance actions return promise of the action.
33704          *
33705          *   The Resource instances and collection have these additional properties:
33706          *
33707          *   - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
33708          *     instance or collection.
33709          *
33710          *     On success, the promise is resolved with the same resource instance or collection object,
33711          *     updated with data from server. This makes it easy to use in
33712          *     {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
33713          *     rendering until the resource(s) are loaded.
33714          *
33715          *     On failure, the promise is resolved with the {@link ng.$http http response} object, without
33716          *     the `resource` property.
33717          *
33718          *     If an interceptor object was provided, the promise will instead be resolved with the value
33719          *     returned by the interceptor.
33720          *
33721          *   - `$resolved`: `true` after first server interaction is completed (either with success or
33722          *      rejection), `false` before that. Knowing if the Resource has been resolved is useful in
33723          *      data-binding.
33724          *
33725          * @example
33726          *
33727          * # Credit card resource
33728          *
33729          * ```js
33730              // Define CreditCard class
33731              var CreditCard = $resource('/user/:userId/card/:cardId',
33732               {userId:123, cardId:'@id'}, {
33733                charge: {method:'POST', params:{charge:true}}
33734               });
33735
33736              // We can retrieve a collection from the server
33737              var cards = CreditCard.query(function() {
33738                // GET: /user/123/card
33739                // server returns: [ {id:456, number:'1234', name:'Smith'} ];
33740
33741                var card = cards[0];
33742                // each item is an instance of CreditCard
33743                expect(card instanceof CreditCard).toEqual(true);
33744                card.name = "J. Smith";
33745                // non GET methods are mapped onto the instances
33746                card.$save();
33747                // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
33748                // server returns: {id:456, number:'1234', name: 'J. Smith'};
33749
33750                // our custom method is mapped as well.
33751                card.$charge({amount:9.99});
33752                // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
33753              });
33754
33755              // we can create an instance as well
33756              var newCard = new CreditCard({number:'0123'});
33757              newCard.name = "Mike Smith";
33758              newCard.$save();
33759              // POST: /user/123/card {number:'0123', name:'Mike Smith'}
33760              // server returns: {id:789, number:'0123', name: 'Mike Smith'};
33761              expect(newCard.id).toEqual(789);
33762          * ```
33763          *
33764          * The object returned from this function execution is a resource "class" which has "static" method
33765          * for each action in the definition.
33766          *
33767          * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
33768          * `headers`.
33769          * When the data is returned from the server then the object is an instance of the resource type and
33770          * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
33771          * operations (create, read, update, delete) on server-side data.
33772
33773            ```js
33774              var User = $resource('/user/:userId', {userId:'@id'});
33775              User.get({userId:123}, function(user) {
33776                user.abc = true;
33777                user.$save();
33778              });
33779            ```
33780          *
33781          * It's worth noting that the success callback for `get`, `query` and other methods gets passed
33782          * in the response that came from the server as well as $http header getter function, so one
33783          * could rewrite the above example and get access to http headers as:
33784          *
33785            ```js
33786              var User = $resource('/user/:userId', {userId:'@id'});
33787              User.get({userId:123}, function(u, getResponseHeaders){
33788                u.abc = true;
33789                u.$save(function(u, putResponseHeaders) {
33790                  //u => saved user object
33791                  //putResponseHeaders => $http header getter
33792                });
33793              });
33794            ```
33795          *
33796          * You can also access the raw `$http` promise via the `$promise` property on the object returned
33797          *
33798            ```
33799              var User = $resource('/user/:userId', {userId:'@id'});
33800              User.get({userId:123})
33801                  .$promise.then(function(user) {
33802                    $scope.user = user;
33803                  });
33804            ```
33805
33806          * # Creating a custom 'PUT' request
33807          * In this example we create a custom method on our resource to make a PUT request
33808          * ```js
33809          *    var app = angular.module('app', ['ngResource', 'ngRoute']);
33810          *
33811          *    // Some APIs expect a PUT request in the format URL/object/ID
33812          *    // Here we are creating an 'update' method
33813          *    app.factory('Notes', ['$resource', function($resource) {
33814          *    return $resource('/notes/:id', null,
33815          *        {
33816          *            'update': { method:'PUT' }
33817          *        });
33818          *    }]);
33819          *
33820          *    // In our controller we get the ID from the URL using ngRoute and $routeParams
33821          *    // We pass in $routeParams and our Notes factory along with $scope
33822          *    app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
33823                                               function($scope, $routeParams, Notes) {
33824          *    // First get a note object from the factory
33825          *    var note = Notes.get({ id:$routeParams.id });
33826          *    $id = note.id;
33827          *
33828          *    // Now call update passing in the ID first then the object you are updating
33829          *    Notes.update({ id:$id }, note);
33830          *
33831          *    // This will PUT /notes/ID with the note object in the request payload
33832          *    }]);
33833          * ```
33834          */
33835         angular.module('ngResource', ['ng']).
33836           provider('$resource', function() {
33837             var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
33838             var provider = this;
33839
33840             this.defaults = {
33841               // Strip slashes by default
33842               stripTrailingSlashes: true,
33843
33844               // Default actions configuration
33845               actions: {
33846                 'get': {method: 'GET'},
33847                 'save': {method: 'POST'},
33848                 'query': {method: 'GET', isArray: true},
33849                 'remove': {method: 'DELETE'},
33850                 'delete': {method: 'DELETE'}
33851               }
33852             };
33853
33854             this.$get = ['$http', '$q', function($http, $q) {
33855
33856               var noop = angular.noop,
33857                 forEach = angular.forEach,
33858                 extend = angular.extend,
33859                 copy = angular.copy,
33860                 isFunction = angular.isFunction;
33861
33862               /**
33863                * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
33864                * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
33865                * (pchar) allowed in path segments:
33866                *    segment       = *pchar
33867                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33868                *    pct-encoded   = "%" HEXDIG HEXDIG
33869                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33870                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33871                *                     / "*" / "+" / "," / ";" / "="
33872                */
33873               function encodeUriSegment(val) {
33874                 return encodeUriQuery(val, true).
33875                   replace(/%26/gi, '&').
33876                   replace(/%3D/gi, '=').
33877                   replace(/%2B/gi, '+');
33878               }
33879
33880
33881               /**
33882                * This method is intended for encoding *key* or *value* parts of query component. We need a
33883                * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
33884                * have to be encoded per http://tools.ietf.org/html/rfc3986:
33885                *    query       = *( pchar / "/" / "?" )
33886                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33887                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33888                *    pct-encoded   = "%" HEXDIG HEXDIG
33889                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33890                *                     / "*" / "+" / "," / ";" / "="
33891                */
33892               function encodeUriQuery(val, pctEncodeSpaces) {
33893                 return encodeURIComponent(val).
33894                   replace(/%40/gi, '@').
33895                   replace(/%3A/gi, ':').
33896                   replace(/%24/g, '$').
33897                   replace(/%2C/gi, ',').
33898                   replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
33899               }
33900
33901               function Route(template, defaults) {
33902                 this.template = template;
33903                 this.defaults = extend({}, provider.defaults, defaults);
33904                 this.urlParams = {};
33905               }
33906
33907               Route.prototype = {
33908                 setUrlParams: function(config, params, actionUrl) {
33909                   var self = this,
33910                     url = actionUrl || self.template,
33911                     val,
33912                     encodedVal,
33913                     protocolAndDomain = '';
33914
33915                   var urlParams = self.urlParams = {};
33916                   forEach(url.split(/\W/), function(param) {
33917                     if (param === 'hasOwnProperty') {
33918                       throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
33919                     }
33920                     if (!(new RegExp("^\\d+$").test(param)) && param &&
33921                       (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
33922                       urlParams[param] = true;
33923                     }
33924                   });
33925                   url = url.replace(/\\:/g, ':');
33926                   url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
33927                     protocolAndDomain = match;
33928                     return '';
33929                   });
33930
33931                   params = params || {};
33932                   forEach(self.urlParams, function(_, urlParam) {
33933                     val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
33934                     if (angular.isDefined(val) && val !== null) {
33935                       encodedVal = encodeUriSegment(val);
33936                       url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
33937                         return encodedVal + p1;
33938                       });
33939                     } else {
33940                       url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
33941                           leadingSlashes, tail) {
33942                         if (tail.charAt(0) == '/') {
33943                           return tail;
33944                         } else {
33945                           return leadingSlashes + tail;
33946                         }
33947                       });
33948                     }
33949                   });
33950
33951                   // strip trailing slashes and set the url (unless this behavior is specifically disabled)
33952                   if (self.defaults.stripTrailingSlashes) {
33953                     url = url.replace(/\/+$/, '') || '/';
33954                   }
33955
33956                   // then replace collapse `/.` if found in the last URL path segment before the query
33957                   // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
33958                   url = url.replace(/\/\.(?=\w+($|\?))/, '.');
33959                   // replace escaped `/\.` with `/.`
33960                   config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
33961
33962
33963                   // set params - delegate param encoding to $http
33964                   forEach(params, function(value, key) {
33965                     if (!self.urlParams[key]) {
33966                       config.params = config.params || {};
33967                       config.params[key] = value;
33968                     }
33969                   });
33970                 }
33971               };
33972
33973
33974               function resourceFactory(url, paramDefaults, actions, options) {
33975                 var route = new Route(url, options);
33976
33977                 actions = extend({}, provider.defaults.actions, actions);
33978
33979                 function extractParams(data, actionParams) {
33980                   var ids = {};
33981                   actionParams = extend({}, paramDefaults, actionParams);
33982                   forEach(actionParams, function(value, key) {
33983                     if (isFunction(value)) { value = value(); }
33984                     ids[key] = value && value.charAt && value.charAt(0) == '@' ?
33985                       lookupDottedPath(data, value.substr(1)) : value;
33986                   });
33987                   return ids;
33988                 }
33989
33990                 function defaultResponseInterceptor(response) {
33991                   return response.resource;
33992                 }
33993
33994                 function Resource(value) {
33995                   shallowClearAndCopy(value || {}, this);
33996                 }
33997
33998                 Resource.prototype.toJSON = function() {
33999                   var data = extend({}, this);
34000                   delete data.$promise;
34001                   delete data.$resolved;
34002                   return data;
34003                 };
34004
34005                 forEach(actions, function(action, name) {
34006                   var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
34007
34008                   Resource[name] = function(a1, a2, a3, a4) {
34009                     var params = {}, data, success, error;
34010
34011                     /* jshint -W086 */ /* (purposefully fall through case statements) */
34012                     switch (arguments.length) {
34013                       case 4:
34014                         error = a4;
34015                         success = a3;
34016                       //fallthrough
34017                       case 3:
34018                       case 2:
34019                         if (isFunction(a2)) {
34020                           if (isFunction(a1)) {
34021                             success = a1;
34022                             error = a2;
34023                             break;
34024                           }
34025
34026                           success = a2;
34027                           error = a3;
34028                           //fallthrough
34029                         } else {
34030                           params = a1;
34031                           data = a2;
34032                           success = a3;
34033                           break;
34034                         }
34035                       case 1:
34036                         if (isFunction(a1)) success = a1;
34037                         else if (hasBody) data = a1;
34038                         else params = a1;
34039                         break;
34040                       case 0: break;
34041                       default:
34042                         throw $resourceMinErr('badargs',
34043                           "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
34044                           arguments.length);
34045                     }
34046                     /* jshint +W086 */ /* (purposefully fall through case statements) */
34047
34048                     var isInstanceCall = this instanceof Resource;
34049                     var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
34050                     var httpConfig = {};
34051                     var responseInterceptor = action.interceptor && action.interceptor.response ||
34052                       defaultResponseInterceptor;
34053                     var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
34054                       undefined;
34055
34056                     forEach(action, function(value, key) {
34057                       switch (key) {
34058                         default:
34059                           httpConfig[key] = copy(value);
34060                           break;
34061                         case 'params':
34062                         case 'isArray':
34063                         case 'interceptor':
34064                           break;
34065                         case 'timeout':
34066                           httpConfig[key] = value;
34067                           break;
34068                       }
34069                     });
34070
34071                     if (hasBody) httpConfig.data = data;
34072                     route.setUrlParams(httpConfig,
34073                       extend({}, extractParams(data, action.params || {}), params),
34074                       action.url);
34075
34076                     var promise = $http(httpConfig).then(function(response) {
34077                       var data = response.data,
34078                         promise = value.$promise;
34079
34080                       if (data) {
34081                         // Need to convert action.isArray to boolean in case it is undefined
34082                         // jshint -W018
34083                         if (angular.isArray(data) !== (!!action.isArray)) {
34084                           throw $resourceMinErr('badcfg',
34085                               'Error in resource configuration for action `{0}`. Expected response to ' +
34086                               'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
34087                             angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
34088                         }
34089                         // jshint +W018
34090                         if (action.isArray) {
34091                           value.length = 0;
34092                           forEach(data, function(item) {
34093                             if (typeof item === "object") {
34094                               value.push(new Resource(item));
34095                             } else {
34096                               // Valid JSON values may be string literals, and these should not be converted
34097                               // into objects. These items will not have access to the Resource prototype
34098                               // methods, but unfortunately there
34099                               value.push(item);
34100                             }
34101                           });
34102                         } else {
34103                           shallowClearAndCopy(data, value);
34104                           value.$promise = promise;
34105                         }
34106                       }
34107
34108                       value.$resolved = true;
34109
34110                       response.resource = value;
34111
34112                       return response;
34113                     }, function(response) {
34114                       value.$resolved = true;
34115
34116                       (error || noop)(response);
34117
34118                       return $q.reject(response);
34119                     });
34120
34121                     promise = promise.then(
34122                       function(response) {
34123                         var value = responseInterceptor(response);
34124                         (success || noop)(value, response.headers);
34125                         return value;
34126                       },
34127                       responseErrorInterceptor);
34128
34129                     if (!isInstanceCall) {
34130                       // we are creating instance / collection
34131                       // - set the initial promise
34132                       // - return the instance / collection
34133                       value.$promise = promise;
34134                       value.$resolved = false;
34135
34136                       return value;
34137                     }
34138
34139                     // instance call
34140                     return promise;
34141                   };
34142
34143
34144                   Resource.prototype['$' + name] = function(params, success, error) {
34145                     if (isFunction(params)) {
34146                       error = success; success = params; params = {};
34147                     }
34148                     var result = Resource[name].call(this, params, this, success, error);
34149                     return result.$promise || result;
34150                   };
34151                 });
34152
34153                 Resource.bind = function(additionalParamDefaults) {
34154                   return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
34155                 };
34156
34157                 return Resource;
34158               }
34159
34160               return resourceFactory;
34161             }];
34162           });
34163
34164
34165         })(window, window.angular);
34166
34167
34168 /***/ },
34169 /* 6 */
34170 /***/ function(module, exports, __webpack_require__) {
34171
34172         __webpack_require__(7);
34173
34174         module.exports = 'ui.bootstrap';
34175
34176
34177 /***/ },
34178 /* 7 */
34179 /***/ function(module, exports) {
34180
34181         /*
34182          * angular-ui-bootstrap
34183          * http://angular-ui.github.io/bootstrap/
34184
34185          * Version: 1.0.0 - 2016-01-08
34186          * License: MIT
34187          */
34188         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"]);
34189         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"]);
34190         angular.module('ui.bootstrap.collapse', [])
34191
34192           .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
34193             var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
34194             return {
34195               link: function(scope, element, attrs) {
34196                 if (!scope.$eval(attrs.uibCollapse)) {
34197                   element.addClass('in')
34198                     .addClass('collapse')
34199                     .css({height: 'auto'});
34200                 }
34201
34202                 function expand() {
34203                   element.removeClass('collapse')
34204                     .addClass('collapsing')
34205                     .attr('aria-expanded', true)
34206                     .attr('aria-hidden', false);
34207
34208                   if ($animateCss) {
34209                     $animateCss(element, {
34210                       addClass: 'in',
34211                       easing: 'ease',
34212                       to: { height: element[0].scrollHeight + 'px' }
34213                     }).start()['finally'](expandDone);
34214                   } else {
34215                     $animate.addClass(element, 'in', {
34216                       to: { height: element[0].scrollHeight + 'px' }
34217                     }).then(expandDone);
34218                   }
34219                 }
34220
34221                 function expandDone() {
34222                   element.removeClass('collapsing')
34223                     .addClass('collapse')
34224                     .css({height: 'auto'});
34225                 }
34226
34227                 function collapse() {
34228                   if (!element.hasClass('collapse') && !element.hasClass('in')) {
34229                     return collapseDone();
34230                   }
34231
34232                   element
34233                     // IMPORTANT: The height must be set before adding "collapsing" class.
34234                     // Otherwise, the browser attempts to animate from height 0 (in
34235                     // collapsing class) to the given height here.
34236                     .css({height: element[0].scrollHeight + 'px'})
34237                     // initially all panel collapse have the collapse class, this removal
34238                     // prevents the animation from jumping to collapsed state
34239                     .removeClass('collapse')
34240                     .addClass('collapsing')
34241                     .attr('aria-expanded', false)
34242                     .attr('aria-hidden', true);
34243
34244                   if ($animateCss) {
34245                     $animateCss(element, {
34246                       removeClass: 'in',
34247                       to: {height: '0'}
34248                     }).start()['finally'](collapseDone);
34249                   } else {
34250                     $animate.removeClass(element, 'in', {
34251                       to: {height: '0'}
34252                     }).then(collapseDone);
34253                   }
34254                 }
34255
34256                 function collapseDone() {
34257                   element.css({height: '0'}); // Required so that collapse works when animation is disabled
34258                   element.removeClass('collapsing')
34259                     .addClass('collapse');
34260                 }
34261
34262                 scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
34263                   if (shouldCollapse) {
34264                     collapse();
34265                   } else {
34266                     expand();
34267                   }
34268                 });
34269               }
34270             };
34271           }]);
34272
34273         angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
34274
34275         .constant('uibAccordionConfig', {
34276           closeOthers: true
34277         })
34278
34279         .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
34280           // This array keeps track of the accordion groups
34281           this.groups = [];
34282
34283           // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
34284           this.closeOthers = function(openGroup) {
34285             var closeOthers = angular.isDefined($attrs.closeOthers) ?
34286               $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
34287             if (closeOthers) {
34288               angular.forEach(this.groups, function(group) {
34289                 if (group !== openGroup) {
34290                   group.isOpen = false;
34291                 }
34292               });
34293             }
34294           };
34295
34296           // This is called from the accordion-group directive to add itself to the accordion
34297           this.addGroup = function(groupScope) {
34298             var that = this;
34299             this.groups.push(groupScope);
34300
34301             groupScope.$on('$destroy', function(event) {
34302               that.removeGroup(groupScope);
34303             });
34304           };
34305
34306           // This is called from the accordion-group directive when to remove itself
34307           this.removeGroup = function(group) {
34308             var index = this.groups.indexOf(group);
34309             if (index !== -1) {
34310               this.groups.splice(index, 1);
34311             }
34312           };
34313         }])
34314
34315         // The accordion directive simply sets up the directive controller
34316         // and adds an accordion CSS class to itself element.
34317         .directive('uibAccordion', function() {
34318           return {
34319             controller: 'UibAccordionController',
34320             controllerAs: 'accordion',
34321             transclude: true,
34322             templateUrl: function(element, attrs) {
34323               return attrs.templateUrl || 'uib/template/accordion/accordion.html';
34324             }
34325           };
34326         })
34327
34328         // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
34329         .directive('uibAccordionGroup', function() {
34330           return {
34331             require: '^uibAccordion',         // We need this directive to be inside an accordion
34332             transclude: true,              // It transcludes the contents of the directive into the template
34333             replace: true,                // The element containing the directive will be replaced with the template
34334             templateUrl: function(element, attrs) {
34335               return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
34336             },
34337             scope: {
34338               heading: '@',               // Interpolate the heading attribute onto this scope
34339               isOpen: '=?',
34340               isDisabled: '=?'
34341             },
34342             controller: function() {
34343               this.setHeading = function(element) {
34344                 this.heading = element;
34345               };
34346             },
34347             link: function(scope, element, attrs, accordionCtrl) {
34348               accordionCtrl.addGroup(scope);
34349
34350               scope.openClass = attrs.openClass || 'panel-open';
34351               scope.panelClass = attrs.panelClass || 'panel-default';
34352               scope.$watch('isOpen', function(value) {
34353                 element.toggleClass(scope.openClass, !!value);
34354                 if (value) {
34355                   accordionCtrl.closeOthers(scope);
34356                 }
34357               });
34358
34359               scope.toggleOpen = function($event) {
34360                 if (!scope.isDisabled) {
34361                   if (!$event || $event.which === 32) {
34362                     scope.isOpen = !scope.isOpen;
34363                   }
34364                 }
34365               };
34366             }
34367           };
34368         })
34369
34370         // Use accordion-heading below an accordion-group to provide a heading containing HTML
34371         .directive('uibAccordionHeading', function() {
34372           return {
34373             transclude: true,   // Grab the contents to be used as the heading
34374             template: '',       // In effect remove this element!
34375             replace: true,
34376             require: '^uibAccordionGroup',
34377             link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
34378               // Pass the heading to the accordion-group controller
34379               // so that it can be transcluded into the right place in the template
34380               // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
34381               accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
34382             }
34383           };
34384         })
34385
34386         // Use in the accordion-group template to indicate where you want the heading to be transcluded
34387         // You must provide the property on the accordion-group controller that will hold the transcluded element
34388         .directive('uibAccordionTransclude', function() {
34389           return {
34390             require: '^uibAccordionGroup',
34391             link: function(scope, element, attrs, controller) {
34392               scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
34393                 if (heading) {
34394                   element.find('span').html('');
34395                   element.find('span').append(heading);
34396                 }
34397               });
34398             }
34399           };
34400         });
34401
34402         angular.module('ui.bootstrap.alert', [])
34403
34404         .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
34405           $scope.closeable = !!$attrs.close;
34406
34407           var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
34408             $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
34409
34410           if (dismissOnTimeout) {
34411             $timeout(function() {
34412               $scope.close();
34413             }, parseInt(dismissOnTimeout, 10));
34414           }
34415         }])
34416
34417         .directive('uibAlert', function() {
34418           return {
34419             controller: 'UibAlertController',
34420             controllerAs: 'alert',
34421             templateUrl: function(element, attrs) {
34422               return attrs.templateUrl || 'uib/template/alert/alert.html';
34423             },
34424             transclude: true,
34425             replace: true,
34426             scope: {
34427               type: '@',
34428               close: '&'
34429             }
34430           };
34431         });
34432
34433         angular.module('ui.bootstrap.buttons', [])
34434
34435         .constant('uibButtonConfig', {
34436           activeClass: 'active',
34437           toggleEvent: 'click'
34438         })
34439
34440         .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
34441           this.activeClass = buttonConfig.activeClass || 'active';
34442           this.toggleEvent = buttonConfig.toggleEvent || 'click';
34443         }])
34444
34445         .directive('uibBtnRadio', ['$parse', function($parse) {
34446           return {
34447             require: ['uibBtnRadio', 'ngModel'],
34448             controller: 'UibButtonsController',
34449             controllerAs: 'buttons',
34450             link: function(scope, element, attrs, ctrls) {
34451               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34452               var uncheckableExpr = $parse(attrs.uibUncheckable);
34453
34454               element.find('input').css({display: 'none'});
34455
34456               //model -> UI
34457               ngModelCtrl.$render = function() {
34458                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
34459               };
34460
34461               //ui->model
34462               element.on(buttonsCtrl.toggleEvent, function() {
34463                 if (attrs.disabled) {
34464                   return;
34465                 }
34466
34467                 var isActive = element.hasClass(buttonsCtrl.activeClass);
34468
34469                 if (!isActive || angular.isDefined(attrs.uncheckable)) {
34470                   scope.$apply(function() {
34471                     ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
34472                     ngModelCtrl.$render();
34473                   });
34474                 }
34475               });
34476
34477               if (attrs.uibUncheckable) {
34478                 scope.$watch(uncheckableExpr, function(uncheckable) {
34479                   attrs.$set('uncheckable', uncheckable ? '' : null);
34480                 });
34481               }
34482             }
34483           };
34484         }])
34485
34486         .directive('uibBtnCheckbox', function() {
34487           return {
34488             require: ['uibBtnCheckbox', 'ngModel'],
34489             controller: 'UibButtonsController',
34490             controllerAs: 'button',
34491             link: function(scope, element, attrs, ctrls) {
34492               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34493
34494               element.find('input').css({display: 'none'});
34495
34496               function getTrueValue() {
34497                 return getCheckboxValue(attrs.btnCheckboxTrue, true);
34498               }
34499
34500               function getFalseValue() {
34501                 return getCheckboxValue(attrs.btnCheckboxFalse, false);
34502               }
34503
34504               function getCheckboxValue(attribute, defaultValue) {
34505                 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
34506               }
34507
34508               //model -> UI
34509               ngModelCtrl.$render = function() {
34510                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
34511               };
34512
34513               //ui->model
34514               element.on(buttonsCtrl.toggleEvent, function() {
34515                 if (attrs.disabled) {
34516                   return;
34517                 }
34518
34519                 scope.$apply(function() {
34520                   ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
34521                   ngModelCtrl.$render();
34522                 });
34523               });
34524             }
34525           };
34526         });
34527
34528         angular.module('ui.bootstrap.carousel', [])
34529
34530         .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
34531           var self = this,
34532             slides = self.slides = $scope.slides = [],
34533             SLIDE_DIRECTION = 'uib-slideDirection',
34534             currentIndex = -1,
34535             currentInterval, isPlaying, bufferedTransitions = [];
34536           self.currentSlide = null;
34537
34538           var destroyed = false;
34539
34540           self.addSlide = function(slide, element) {
34541             slide.$element = element;
34542             slides.push(slide);
34543             //if this is the first slide or the slide is set to active, select it
34544             if (slides.length === 1 || slide.active) {
34545               if ($scope.$currentTransition) {
34546                 $scope.$currentTransition = null;
34547               }
34548
34549               self.select(slides[slides.length - 1]);
34550               if (slides.length === 1) {
34551                 $scope.play();
34552               }
34553             } else {
34554               slide.active = false;
34555             }
34556           };
34557
34558           self.getCurrentIndex = function() {
34559             if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
34560               return +self.currentSlide.index;
34561             }
34562             return currentIndex;
34563           };
34564
34565           self.next = $scope.next = function() {
34566             var newIndex = (self.getCurrentIndex() + 1) % slides.length;
34567
34568             if (newIndex === 0 && $scope.noWrap()) {
34569               $scope.pause();
34570               return;
34571             }
34572
34573             return self.select(getSlideByIndex(newIndex), 'next');
34574           };
34575
34576           self.prev = $scope.prev = function() {
34577             var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
34578
34579             if ($scope.noWrap() && newIndex === slides.length - 1) {
34580               $scope.pause();
34581               return;
34582             }
34583
34584             return self.select(getSlideByIndex(newIndex), 'prev');
34585           };
34586
34587           self.removeSlide = function(slide) {
34588             if (angular.isDefined(slide.index)) {
34589               slides.sort(function(a, b) {
34590                 return +a.index > +b.index;
34591               });
34592             }
34593
34594             var bufferedIndex = bufferedTransitions.indexOf(slide);
34595             if (bufferedIndex !== -1) {
34596               bufferedTransitions.splice(bufferedIndex, 1);
34597             }
34598             //get the index of the slide inside the carousel
34599             var index = slides.indexOf(slide);
34600             slides.splice(index, 1);
34601             $timeout(function() {
34602               if (slides.length > 0 && slide.active) {
34603                 if (index >= slides.length) {
34604                   self.select(slides[index - 1]);
34605                 } else {
34606                   self.select(slides[index]);
34607                 }
34608               } else if (currentIndex > index) {
34609                 currentIndex--;
34610               }
34611             });
34612
34613             //clean the currentSlide when no more slide
34614             if (slides.length === 0) {
34615               self.currentSlide = null;
34616               clearBufferedTransitions();
34617             }
34618           };
34619
34620           /* direction: "prev" or "next" */
34621           self.select = $scope.select = function(nextSlide, direction) {
34622             var nextIndex = $scope.indexOfSlide(nextSlide);
34623             //Decide direction if it's not given
34624             if (direction === undefined) {
34625               direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34626             }
34627             //Prevent this user-triggered transition from occurring if there is already one in progress
34628             if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
34629               goNext(nextSlide, nextIndex, direction);
34630             } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
34631               bufferedTransitions.push(nextSlide);
34632             }
34633           };
34634
34635           /* Allow outside people to call indexOf on slides array */
34636           $scope.indexOfSlide = function(slide) {
34637             return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
34638           };
34639
34640           $scope.isActive = function(slide) {
34641             return self.currentSlide === slide;
34642           };
34643
34644           $scope.pause = function() {
34645             if (!$scope.noPause) {
34646               isPlaying = false;
34647               resetTimer();
34648             }
34649           };
34650
34651           $scope.play = function() {
34652             if (!isPlaying) {
34653               isPlaying = true;
34654               restartTimer();
34655             }
34656           };
34657
34658           $scope.$on('$destroy', function() {
34659             destroyed = true;
34660             resetTimer();
34661           });
34662
34663           $scope.$watch('noTransition', function(noTransition) {
34664             $animate.enabled($element, !noTransition);
34665           });
34666
34667           $scope.$watch('interval', restartTimer);
34668
34669           $scope.$watchCollection('slides', resetTransition);
34670
34671           function clearBufferedTransitions() {
34672             while (bufferedTransitions.length) {
34673               bufferedTransitions.shift();
34674             }
34675           }
34676
34677           function getSlideByIndex(index) {
34678             if (angular.isUndefined(slides[index].index)) {
34679               return slides[index];
34680             }
34681             for (var i = 0, l = slides.length; i < l; ++i) {
34682               if (slides[i].index === index) {
34683                 return slides[i];
34684               }
34685             }
34686           }
34687
34688           function goNext(slide, index, direction) {
34689             if (destroyed) { return; }
34690
34691             angular.extend(slide, {direction: direction, active: true});
34692             angular.extend(self.currentSlide || {}, {direction: direction, active: false});
34693             if ($animate.enabled($element) && !$scope.$currentTransition &&
34694               slide.$element && self.slides.length > 1) {
34695               slide.$element.data(SLIDE_DIRECTION, slide.direction);
34696               if (self.currentSlide && self.currentSlide.$element) {
34697                 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
34698               }
34699
34700               $scope.$currentTransition = true;
34701               $animate.on('addClass', slide.$element, function(element, phase) {
34702                 if (phase === 'close') {
34703                   $scope.$currentTransition = null;
34704                   $animate.off('addClass', element);
34705                   if (bufferedTransitions.length) {
34706                     var nextSlide = bufferedTransitions.pop();
34707                     var nextIndex = $scope.indexOfSlide(nextSlide);
34708                     var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34709                     clearBufferedTransitions();
34710
34711                     goNext(nextSlide, nextIndex, nextDirection);
34712                   }
34713                 }
34714               });
34715             }
34716
34717             self.currentSlide = slide;
34718             currentIndex = index;
34719
34720             //every time you change slides, reset the timer
34721             restartTimer();
34722           }
34723
34724           function resetTimer() {
34725             if (currentInterval) {
34726               $interval.cancel(currentInterval);
34727               currentInterval = null;
34728             }
34729           }
34730
34731           function resetTransition(slides) {
34732             if (!slides.length) {
34733               $scope.$currentTransition = null;
34734               clearBufferedTransitions();
34735             }
34736           }
34737
34738           function restartTimer() {
34739             resetTimer();
34740             var interval = +$scope.interval;
34741             if (!isNaN(interval) && interval > 0) {
34742               currentInterval = $interval(timerFn, interval);
34743             }
34744           }
34745
34746           function timerFn() {
34747             var interval = +$scope.interval;
34748             if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
34749               $scope.next();
34750             } else {
34751               $scope.pause();
34752             }
34753           }
34754         }])
34755
34756         .directive('uibCarousel', function() {
34757           return {
34758             transclude: true,
34759             replace: true,
34760             controller: 'UibCarouselController',
34761             controllerAs: 'carousel',
34762             templateUrl: function(element, attrs) {
34763               return attrs.templateUrl || 'uib/template/carousel/carousel.html';
34764             },
34765             scope: {
34766               interval: '=',
34767               noTransition: '=',
34768               noPause: '=',
34769               noWrap: '&'
34770             }
34771           };
34772         })
34773
34774         .directive('uibSlide', function() {
34775           return {
34776             require: '^uibCarousel',
34777             transclude: true,
34778             replace: true,
34779             templateUrl: function(element, attrs) {
34780               return attrs.templateUrl || 'uib/template/carousel/slide.html';
34781             },
34782             scope: {
34783               active: '=?',
34784               actual: '=?',
34785               index: '=?'
34786             },
34787             link: function (scope, element, attrs, carouselCtrl) {
34788               carouselCtrl.addSlide(scope, element);
34789               //when the scope is destroyed then remove the slide from the current slides array
34790               scope.$on('$destroy', function() {
34791                 carouselCtrl.removeSlide(scope);
34792               });
34793
34794               scope.$watch('active', function(active) {
34795                 if (active) {
34796                   carouselCtrl.select(scope);
34797                 }
34798               });
34799             }
34800           };
34801         })
34802
34803         .animation('.item', ['$animateCss',
34804         function($animateCss) {
34805           var SLIDE_DIRECTION = 'uib-slideDirection';
34806
34807           function removeClass(element, className, callback) {
34808             element.removeClass(className);
34809             if (callback) {
34810               callback();
34811             }
34812           }
34813
34814           return {
34815             beforeAddClass: function(element, className, done) {
34816               if (className === 'active') {
34817                 var stopped = false;
34818                 var direction = element.data(SLIDE_DIRECTION);
34819                 var directionClass = direction === 'next' ? 'left' : 'right';
34820                 var removeClassFn = removeClass.bind(this, element,
34821                   directionClass + ' ' + direction, done);
34822                 element.addClass(direction);
34823
34824                 $animateCss(element, {addClass: directionClass})
34825                   .start()
34826                   .done(removeClassFn);
34827
34828                 return function() {
34829                   stopped = true;
34830                 };
34831               }
34832               done();
34833             },
34834             beforeRemoveClass: function (element, className, done) {
34835               if (className === 'active') {
34836                 var stopped = false;
34837                 var direction = element.data(SLIDE_DIRECTION);
34838                 var directionClass = direction === 'next' ? 'left' : 'right';
34839                 var removeClassFn = removeClass.bind(this, element, directionClass, done);
34840
34841                 $animateCss(element, {addClass: directionClass})
34842                   .start()
34843                   .done(removeClassFn);
34844
34845                 return function() {
34846                   stopped = true;
34847                 };
34848               }
34849               done();
34850             }
34851           };
34852         }]);
34853
34854         angular.module('ui.bootstrap.dateparser', [])
34855
34856         .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
34857           // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
34858           var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
34859
34860           var localeId;
34861           var formatCodeToRegex;
34862
34863           this.init = function() {
34864             localeId = $locale.id;
34865
34866             this.parsers = {};
34867
34868             formatCodeToRegex = [
34869               {
34870                 key: 'yyyy',
34871                 regex: '\\d{4}',
34872                 apply: function(value) { this.year = +value; }
34873               },
34874               {
34875                 key: 'yy',
34876                 regex: '\\d{2}',
34877                 apply: function(value) { this.year = +value + 2000; }
34878               },
34879               {
34880                 key: 'y',
34881                 regex: '\\d{1,4}',
34882                 apply: function(value) { this.year = +value; }
34883               },
34884               {
34885                 key: 'M!',
34886                 regex: '0?[1-9]|1[0-2]',
34887                 apply: function(value) { this.month = value - 1; }
34888               },
34889               {
34890                 key: 'MMMM',
34891                 regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
34892                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
34893               },
34894               {
34895                 key: 'MMM',
34896                 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
34897                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
34898               },
34899               {
34900                 key: 'MM',
34901                 regex: '0[1-9]|1[0-2]',
34902                 apply: function(value) { this.month = value - 1; }
34903               },
34904               {
34905                 key: 'M',
34906                 regex: '[1-9]|1[0-2]',
34907                 apply: function(value) { this.month = value - 1; }
34908               },
34909               {
34910                 key: 'd!',
34911                 regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
34912                 apply: function(value) { this.date = +value; }
34913               },
34914               {
34915                 key: 'dd',
34916                 regex: '[0-2][0-9]{1}|3[0-1]{1}',
34917                 apply: function(value) { this.date = +value; }
34918               },
34919               {
34920                 key: 'd',
34921                 regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
34922                 apply: function(value) { this.date = +value; }
34923               },
34924               {
34925                 key: 'EEEE',
34926                 regex: $locale.DATETIME_FORMATS.DAY.join('|')
34927               },
34928               {
34929                 key: 'EEE',
34930                 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
34931               },
34932               {
34933                 key: 'HH',
34934                 regex: '(?:0|1)[0-9]|2[0-3]',
34935                 apply: function(value) { this.hours = +value; }
34936               },
34937               {
34938                 key: 'hh',
34939                 regex: '0[0-9]|1[0-2]',
34940                 apply: function(value) { this.hours = +value; }
34941               },
34942               {
34943                 key: 'H',
34944                 regex: '1?[0-9]|2[0-3]',
34945                 apply: function(value) { this.hours = +value; }
34946               },
34947               {
34948                 key: 'h',
34949                 regex: '[0-9]|1[0-2]',
34950                 apply: function(value) { this.hours = +value; }
34951               },
34952               {
34953                 key: 'mm',
34954                 regex: '[0-5][0-9]',
34955                 apply: function(value) { this.minutes = +value; }
34956               },
34957               {
34958                 key: 'm',
34959                 regex: '[0-9]|[1-5][0-9]',
34960                 apply: function(value) { this.minutes = +value; }
34961               },
34962               {
34963                 key: 'sss',
34964                 regex: '[0-9][0-9][0-9]',
34965                 apply: function(value) { this.milliseconds = +value; }
34966               },
34967               {
34968                 key: 'ss',
34969                 regex: '[0-5][0-9]',
34970                 apply: function(value) { this.seconds = +value; }
34971               },
34972               {
34973                 key: 's',
34974                 regex: '[0-9]|[1-5][0-9]',
34975                 apply: function(value) { this.seconds = +value; }
34976               },
34977               {
34978                 key: 'a',
34979                 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
34980                 apply: function(value) {
34981                   if (this.hours === 12) {
34982                     this.hours = 0;
34983                   }
34984
34985                   if (value === 'PM') {
34986                     this.hours += 12;
34987                   }
34988                 }
34989               },
34990               {
34991                 key: 'Z',
34992                 regex: '[+-]\\d{4}',
34993                 apply: function(value) {
34994                   var matches = value.match(/([+-])(\d{2})(\d{2})/),
34995                     sign = matches[1],
34996                     hours = matches[2],
34997                     minutes = matches[3];
34998                   this.hours += toInt(sign + hours);
34999                   this.minutes += toInt(sign + minutes);
35000                 }
35001               },
35002               {
35003                 key: 'ww',
35004                 regex: '[0-4][0-9]|5[0-3]'
35005               },
35006               {
35007                 key: 'w',
35008                 regex: '[0-9]|[1-4][0-9]|5[0-3]'
35009               },
35010               {
35011                 key: 'GGGG',
35012                 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s')
35013               },
35014               {
35015                 key: 'GGG',
35016                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35017               },
35018               {
35019                 key: 'GG',
35020                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35021               },
35022               {
35023                 key: 'G',
35024                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35025               }
35026             ];
35027           };
35028
35029           this.init();
35030
35031           function createParser(format) {
35032             var map = [], regex = format.split('');
35033
35034             // check for literal values
35035             var quoteIndex = format.indexOf('\'');
35036             if (quoteIndex > -1) {
35037               var inLiteral = false;
35038               format = format.split('');
35039               for (var i = quoteIndex; i < format.length; i++) {
35040                 if (inLiteral) {
35041                   if (format[i] === '\'') {
35042                     if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
35043                       format[i+1] = '$';
35044                       regex[i+1] = '';
35045                     } else { // end of literal
35046                       regex[i] = '';
35047                       inLiteral = false;
35048                     }
35049                   }
35050                   format[i] = '$';
35051                 } else {
35052                   if (format[i] === '\'') { // start of literal
35053                     format[i] = '$';
35054                     regex[i] = '';
35055                     inLiteral = true;
35056                   }
35057                 }
35058               }
35059
35060               format = format.join('');
35061             }
35062
35063             angular.forEach(formatCodeToRegex, function(data) {
35064               var index = format.indexOf(data.key);
35065
35066               if (index > -1) {
35067                 format = format.split('');
35068
35069                 regex[index] = '(' + data.regex + ')';
35070                 format[index] = '$'; // Custom symbol to define consumed part of format
35071                 for (var i = index + 1, n = index + data.key.length; i < n; i++) {
35072                   regex[i] = '';
35073                   format[i] = '$';
35074                 }
35075                 format = format.join('');
35076
35077                 map.push({
35078                   index: index,
35079                   apply: data.apply,
35080                   matcher: data.regex
35081                 });
35082               }
35083             });
35084
35085             return {
35086               regex: new RegExp('^' + regex.join('') + '$'),
35087               map: orderByFilter(map, 'index')
35088             };
35089           }
35090
35091           this.parse = function(input, format, baseDate) {
35092             if (!angular.isString(input) || !format) {
35093               return input;
35094             }
35095
35096             format = $locale.DATETIME_FORMATS[format] || format;
35097             format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
35098
35099             if ($locale.id !== localeId) {
35100               this.init();
35101             }
35102
35103             if (!this.parsers[format]) {
35104               this.parsers[format] = createParser(format);
35105             }
35106
35107             var parser = this.parsers[format],
35108                 regex = parser.regex,
35109                 map = parser.map,
35110                 results = input.match(regex),
35111                 tzOffset = false;
35112             if (results && results.length) {
35113               var fields, dt;
35114               if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
35115                 fields = {
35116                   year: baseDate.getFullYear(),
35117                   month: baseDate.getMonth(),
35118                   date: baseDate.getDate(),
35119                   hours: baseDate.getHours(),
35120                   minutes: baseDate.getMinutes(),
35121                   seconds: baseDate.getSeconds(),
35122                   milliseconds: baseDate.getMilliseconds()
35123                 };
35124               } else {
35125                 if (baseDate) {
35126                   $log.warn('dateparser:', 'baseDate is not a valid date');
35127                 }
35128                 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
35129               }
35130
35131               for (var i = 1, n = results.length; i < n; i++) {
35132                 var mapper = map[i - 1];
35133                 if (mapper.matcher === 'Z') {
35134                   tzOffset = true;
35135                 }
35136
35137                 if (mapper.apply) {
35138                   mapper.apply.call(fields, results[i]);
35139                 }
35140               }
35141
35142               var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
35143                 Date.prototype.setFullYear;
35144               var timesetter = tzOffset ? Date.prototype.setUTCHours :
35145                 Date.prototype.setHours;
35146
35147               if (isValid(fields.year, fields.month, fields.date)) {
35148                 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
35149                   dt = new Date(baseDate);
35150                   datesetter.call(dt, fields.year, fields.month, fields.date);
35151                   timesetter.call(dt, fields.hours, fields.minutes,
35152                     fields.seconds, fields.milliseconds);
35153                 } else {
35154                   dt = new Date(0);
35155                   datesetter.call(dt, fields.year, fields.month, fields.date);
35156                   timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
35157                     fields.seconds || 0, fields.milliseconds || 0);
35158                 }
35159               }
35160
35161               return dt;
35162             }
35163           };
35164
35165           // Check if date is valid for specific month (and year for February).
35166           // Month: 0 = Jan, 1 = Feb, etc
35167           function isValid(year, month, date) {
35168             if (date < 1) {
35169               return false;
35170             }
35171
35172             if (month === 1 && date > 28) {
35173               return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
35174             }
35175
35176             if (month === 3 || month === 5 || month === 8 || month === 10) {
35177               return date < 31;
35178             }
35179
35180             return true;
35181           }
35182
35183           function toInt(str) {
35184             return parseInt(str, 10);
35185           }
35186
35187           this.toTimezone = toTimezone;
35188           this.fromTimezone = fromTimezone;
35189           this.timezoneToOffset = timezoneToOffset;
35190           this.addDateMinutes = addDateMinutes;
35191           this.convertTimezoneToLocal = convertTimezoneToLocal;
35192           
35193           function toTimezone(date, timezone) {
35194             return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
35195           }
35196
35197           function fromTimezone(date, timezone) {
35198             return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
35199           }
35200
35201           //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
35202           function timezoneToOffset(timezone, fallback) {
35203             var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
35204             return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
35205           }
35206
35207           function addDateMinutes(date, minutes) {
35208             date = new Date(date.getTime());
35209             date.setMinutes(date.getMinutes() + minutes);
35210             return date;
35211           }
35212
35213           function convertTimezoneToLocal(date, timezone, reverse) {
35214             reverse = reverse ? -1 : 1;
35215             var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
35216             return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
35217           }
35218         }]);
35219
35220         // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
35221         // at most one element.
35222         angular.module('ui.bootstrap.isClass', [])
35223         .directive('uibIsClass', [
35224                  '$animate',
35225         function ($animate) {
35226           //                    11111111          22222222
35227           var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
35228           //                    11111111           22222222
35229           var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
35230
35231           var dataPerTracked = {};
35232
35233           return {
35234             restrict: 'A',
35235             compile: function (tElement, tAttrs) {
35236               var linkedScopes = [];
35237               var instances = [];
35238               var expToData = {};
35239               var lastActivated = null;
35240               var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
35241               var onExp = onExpMatches[2];
35242               var expsStr = onExpMatches[1];
35243               var exps = expsStr.split(',');
35244
35245               return linkFn;
35246
35247               function linkFn(scope, element, attrs) {
35248                 linkedScopes.push(scope);
35249                 instances.push({
35250                   scope: scope,
35251                   element: element
35252                 });
35253
35254                 exps.forEach(function (exp, k) {
35255                   addForExp(exp, scope);
35256                 });
35257
35258                 scope.$on('$destroy', removeScope);
35259               }
35260
35261               function addForExp(exp, scope) {
35262                 var matches = exp.match(IS_REGEXP);
35263                 var clazz = scope.$eval(matches[1]);
35264                 var compareWithExp = matches[2];
35265                 var data = expToData[exp];
35266                 if (!data) {
35267                   var watchFn = function (compareWithVal) {
35268                     var newActivated = null;
35269                     instances.some(function (instance) {
35270                       var thisVal = instance.scope.$eval(onExp);
35271                       if (thisVal === compareWithVal) {
35272                         newActivated = instance;
35273                         return true;
35274                       }
35275                     });
35276                     if (data.lastActivated !== newActivated) {
35277                       if (data.lastActivated) {
35278                         $animate.removeClass(data.lastActivated.element, clazz);
35279                       }
35280                       if (newActivated) {
35281                         $animate.addClass(newActivated.element, clazz);
35282                       }
35283                       data.lastActivated = newActivated;
35284                     }
35285                   };
35286                   expToData[exp] = data = {
35287                     lastActivated: null,
35288                     scope: scope,
35289                     watchFn: watchFn,
35290                     compareWithExp: compareWithExp,
35291                     watcher: scope.$watch(compareWithExp, watchFn)
35292                   };
35293                 }
35294                 data.watchFn(scope.$eval(compareWithExp));
35295               }
35296
35297               function removeScope(e) {
35298                 var removedScope = e.targetScope;
35299                 var index = linkedScopes.indexOf(removedScope);
35300                 linkedScopes.splice(index, 1);
35301                 instances.splice(index, 1);
35302                 if (linkedScopes.length) {
35303                   var newWatchScope = linkedScopes[0];
35304                   angular.forEach(expToData, function (data) {
35305                     if (data.scope === removedScope) {
35306                       data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
35307                       data.scope = newWatchScope;
35308                     }
35309                   });
35310                 }
35311                 else {
35312                   expToData = {};
35313                 }
35314               }
35315             }
35316           };
35317         }]);
35318         angular.module('ui.bootstrap.position', [])
35319
35320         /**
35321          * A set of utility methods for working with the DOM.
35322          * It is meant to be used where we need to absolute-position elements in
35323          * relation to another element (this is the case for tooltips, popovers,
35324          * typeahead suggestions etc.).
35325          */
35326           .factory('$uibPosition', ['$document', '$window', function($document, $window) {
35327             /**
35328              * Used by scrollbarWidth() function to cache scrollbar's width.
35329              * Do not access this variable directly, use scrollbarWidth() instead.
35330              */
35331             var SCROLLBAR_WIDTH;
35332             var OVERFLOW_REGEX = {
35333               normal: /(auto|scroll)/,
35334               hidden: /(auto|scroll|hidden)/
35335             };
35336             var PLACEMENT_REGEX = {
35337               auto: /\s?auto?\s?/i,
35338               primary: /^(top|bottom|left|right)$/,
35339               secondary: /^(top|bottom|left|right|center)$/,
35340               vertical: /^(top|bottom)$/
35341             };
35342
35343             return {
35344
35345               /**
35346                * Provides a raw DOM element from a jQuery/jQLite element.
35347                *
35348                * @param {element} elem - The element to convert.
35349                *
35350                * @returns {element} A HTML element.
35351                */
35352               getRawNode: function(elem) {
35353                 return elem[0] || elem;
35354               },
35355
35356               /**
35357                * Provides a parsed number for a style property.  Strips
35358                * units and casts invalid numbers to 0.
35359                *
35360                * @param {string} value - The style value to parse.
35361                *
35362                * @returns {number} A valid number.
35363                */
35364               parseStyle: function(value) {
35365                 value = parseFloat(value);
35366                 return isFinite(value) ? value : 0;
35367               },
35368
35369               /**
35370                * Provides the closest positioned ancestor.
35371                *
35372                * @param {element} element - The element to get the offest parent for.
35373                *
35374                * @returns {element} The closest positioned ancestor.
35375                */
35376               offsetParent: function(elem) {
35377                 elem = this.getRawNode(elem);
35378
35379                 var offsetParent = elem.offsetParent || $document[0].documentElement;
35380
35381                 function isStaticPositioned(el) {
35382                   return ($window.getComputedStyle(el).position || 'static') === 'static';
35383                 }
35384
35385                 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
35386                   offsetParent = offsetParent.offsetParent;
35387                 }
35388
35389                 return offsetParent || $document[0].documentElement;
35390               },
35391
35392               /**
35393                * Provides the scrollbar width, concept from TWBS measureScrollbar()
35394                * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
35395                *
35396                * @returns {number} The width of the browser scollbar.
35397                */
35398               scrollbarWidth: function() {
35399                 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
35400                   var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
35401                   $document.find('body').append(scrollElem);
35402                   SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
35403                   SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
35404                   scrollElem.remove();
35405                 }
35406
35407                 return SCROLLBAR_WIDTH;
35408               },
35409
35410               /**
35411                * Provides the closest scrollable ancestor.
35412                * A port of the jQuery UI scrollParent method:
35413                * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
35414                *
35415                * @param {element} elem - The element to find the scroll parent of.
35416                * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
35417                *   default is false.
35418                *
35419                * @returns {element} A HTML element.
35420                */
35421               scrollParent: function(elem, includeHidden) {
35422                 elem = this.getRawNode(elem);
35423
35424                 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
35425                 var documentEl = $document[0].documentElement;
35426                 var elemStyle = $window.getComputedStyle(elem);
35427                 var excludeStatic = elemStyle.position === 'absolute';
35428                 var scrollParent = elem.parentElement || documentEl;
35429
35430                 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
35431                   return documentEl;
35432                 }
35433
35434                 while (scrollParent.parentElement && scrollParent !== documentEl) {
35435                   var spStyle = $window.getComputedStyle(scrollParent);
35436                   if (excludeStatic && spStyle.position !== 'static') {
35437                     excludeStatic = false;
35438                   }
35439
35440                   if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
35441                     break;
35442                   }
35443                   scrollParent = scrollParent.parentElement;
35444                 }
35445
35446                 return scrollParent;
35447               },
35448
35449               /**
35450                * Provides read-only equivalent of jQuery's position function:
35451                * http://api.jquery.com/position/ - distance to closest positioned
35452                * ancestor.  Does not account for margins by default like jQuery position.
35453                *
35454                * @param {element} elem - The element to caclulate the position on.
35455                * @param {boolean=} [includeMargins=false] - Should margins be accounted
35456                * for, default is false.
35457                *
35458                * @returns {object} An object with the following properties:
35459                *   <ul>
35460                *     <li>**width**: the width of the element</li>
35461                *     <li>**height**: the height of the element</li>
35462                *     <li>**top**: distance to top edge of offset parent</li>
35463                *     <li>**left**: distance to left edge of offset parent</li>
35464                *   </ul>
35465                */
35466               position: function(elem, includeMagins) {
35467                 elem = this.getRawNode(elem);
35468
35469                 var elemOffset = this.offset(elem);
35470                 if (includeMagins) {
35471                   var elemStyle = $window.getComputedStyle(elem);
35472                   elemOffset.top -= this.parseStyle(elemStyle.marginTop);
35473                   elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
35474                 }
35475                 var parent = this.offsetParent(elem);
35476                 var parentOffset = {top: 0, left: 0};
35477
35478                 if (parent !== $document[0].documentElement) {
35479                   parentOffset = this.offset(parent);
35480                   parentOffset.top += parent.clientTop - parent.scrollTop;
35481                   parentOffset.left += parent.clientLeft - parent.scrollLeft;
35482                 }
35483
35484                 return {
35485                   width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
35486                   height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
35487                   top: Math.round(elemOffset.top - parentOffset.top),
35488                   left: Math.round(elemOffset.left - parentOffset.left)
35489                 };
35490               },
35491
35492               /**
35493                * Provides read-only equivalent of jQuery's offset function:
35494                * http://api.jquery.com/offset/ - distance to viewport.  Does
35495                * not account for borders, margins, or padding on the body
35496                * element.
35497                *
35498                * @param {element} elem - The element to calculate the offset on.
35499                *
35500                * @returns {object} An object with the following properties:
35501                *   <ul>
35502                *     <li>**width**: the width of the element</li>
35503                *     <li>**height**: the height of the element</li>
35504                *     <li>**top**: distance to top edge of viewport</li>
35505                *     <li>**right**: distance to bottom edge of viewport</li>
35506                *   </ul>
35507                */
35508               offset: function(elem) {
35509                 elem = this.getRawNode(elem);
35510
35511                 var elemBCR = elem.getBoundingClientRect();
35512                 return {
35513                   width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
35514                   height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
35515                   top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
35516                   left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
35517                 };
35518               },
35519
35520               /**
35521                * Provides offset distance to the closest scrollable ancestor
35522                * or viewport.  Accounts for border and scrollbar width.
35523                *
35524                * Right and bottom dimensions represent the distance to the
35525                * respective edge of the viewport element.  If the element
35526                * edge extends beyond the viewport, a negative value will be
35527                * reported.
35528                *
35529                * @param {element} elem - The element to get the viewport offset for.
35530                * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
35531                * of the first scrollable element, default is false.
35532                * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
35533                * be accounted for, default is true.
35534                *
35535                * @returns {object} An object with the following properties:
35536                *   <ul>
35537                *     <li>**top**: distance to the top content edge of viewport element</li>
35538                *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
35539                *     <li>**left**: distance to the left content edge of viewport element</li>
35540                *     <li>**right**: distance to the right content edge of viewport element</li>
35541                *   </ul>
35542                */
35543               viewportOffset: function(elem, useDocument, includePadding) {
35544                 elem = this.getRawNode(elem);
35545                 includePadding = includePadding !== false ? true : false;
35546
35547                 var elemBCR = elem.getBoundingClientRect();
35548                 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
35549
35550                 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
35551                 var offsetParentBCR = offsetParent.getBoundingClientRect();
35552
35553                 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
35554                 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
35555                 if (offsetParent === $document[0].documentElement) {
35556                   offsetBCR.top += $window.pageYOffset;
35557                   offsetBCR.left += $window.pageXOffset;
35558                 }
35559                 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
35560                 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
35561
35562                 if (includePadding) {
35563                   var offsetParentStyle = $window.getComputedStyle(offsetParent);
35564                   offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
35565                   offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
35566                   offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
35567                   offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
35568                 }
35569
35570                 return {
35571                   top: Math.round(elemBCR.top - offsetBCR.top),
35572                   bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
35573                   left: Math.round(elemBCR.left - offsetBCR.left),
35574                   right: Math.round(offsetBCR.right - elemBCR.right)
35575                 };
35576               },
35577
35578               /**
35579                * Provides an array of placement values parsed from a placement string.
35580                * Along with the 'auto' indicator, supported placement strings are:
35581                *   <ul>
35582                *     <li>top: element on top, horizontally centered on host element.</li>
35583                *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
35584                *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
35585                *     <li>bottom: element on bottom, horizontally centered on host element.</li>
35586                *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
35587                *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
35588                *     <li>left: element on left, vertically centered on host element.</li>
35589                *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
35590                *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
35591                *     <li>right: element on right, vertically centered on host element.</li>
35592                *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
35593                *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
35594                *   </ul>
35595                * A placement string with an 'auto' indicator is expected to be
35596                * space separated from the placement, i.e: 'auto bottom-left'  If
35597                * the primary and secondary placement values do not match 'top,
35598                * bottom, left, right' then 'top' will be the primary placement and
35599                * 'center' will be the secondary placement.  If 'auto' is passed, true
35600                * will be returned as the 3rd value of the array.
35601                *
35602                * @param {string} placement - The placement string to parse.
35603                *
35604                * @returns {array} An array with the following values
35605                * <ul>
35606                *   <li>**[0]**: The primary placement.</li>
35607                *   <li>**[1]**: The secondary placement.</li>
35608                *   <li>**[2]**: If auto is passed: true, else undefined.</li>
35609                * </ul>
35610                */
35611               parsePlacement: function(placement) {
35612                 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
35613                 if (autoPlace) {
35614                   placement = placement.replace(PLACEMENT_REGEX.auto, '');
35615                 }
35616
35617                 placement = placement.split('-');
35618
35619                 placement[0] = placement[0] || 'top';
35620                 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
35621                   placement[0] = 'top';
35622                 }
35623
35624                 placement[1] = placement[1] || 'center';
35625                 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
35626                   placement[1] = 'center';
35627                 }
35628
35629                 if (autoPlace) {
35630                   placement[2] = true;
35631                 } else {
35632                   placement[2] = false;
35633                 }
35634
35635                 return placement;
35636               },
35637
35638               /**
35639                * Provides coordinates for an element to be positioned relative to
35640                * another element.  Passing 'auto' as part of the placement parameter
35641                * will enable smart placement - where the element fits. i.e:
35642                * 'auto left-top' will check to see if there is enough space to the left
35643                * of the hostElem to fit the targetElem, if not place right (same for secondary
35644                * top placement).  Available space is calculated using the viewportOffset
35645                * function.
35646                *
35647                * @param {element} hostElem - The element to position against.
35648                * @param {element} targetElem - The element to position.
35649                * @param {string=} [placement=top] - The placement for the targetElem,
35650                *   default is 'top'. 'center' is assumed as secondary placement for
35651                *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
35652                *   <ul>
35653                *     <li>top</li>
35654                *     <li>top-right</li>
35655                *     <li>top-left</li>
35656                *     <li>bottom</li>
35657                *     <li>bottom-left</li>
35658                *     <li>bottom-right</li>
35659                *     <li>left</li>
35660                *     <li>left-top</li>
35661                *     <li>left-bottom</li>
35662                *     <li>right</li>
35663                *     <li>right-top</li>
35664                *     <li>right-bottom</li>
35665                *   </ul>
35666                * @param {boolean=} [appendToBody=false] - Should the top and left values returned
35667                *   be calculated from the body element, default is false.
35668                *
35669                * @returns {object} An object with the following properties:
35670                *   <ul>
35671                *     <li>**top**: Value for targetElem top.</li>
35672                *     <li>**left**: Value for targetElem left.</li>
35673                *     <li>**placement**: The resolved placement.</li>
35674                *   </ul>
35675                */
35676               positionElements: function(hostElem, targetElem, placement, appendToBody) {
35677                 hostElem = this.getRawNode(hostElem);
35678                 targetElem = this.getRawNode(targetElem);
35679
35680                 // need to read from prop to support tests.
35681                 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
35682                 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
35683
35684                 placement = this.parsePlacement(placement);
35685
35686                 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
35687                 var targetElemPos = {top: 0, left: 0, placement: ''};
35688
35689                 if (placement[2]) {
35690                   var viewportOffset = this.viewportOffset(hostElem);
35691
35692                   var targetElemStyle = $window.getComputedStyle(targetElem);
35693                   var adjustedSize = {
35694                     width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
35695                     height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
35696                   };
35697
35698                   placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
35699                                  placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
35700                                  placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
35701                                  placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
35702                                  placement[0];
35703
35704                   placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
35705                                  placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
35706                                  placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
35707                                  placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
35708                                  placement[1];
35709
35710                   if (placement[1] === 'center') {
35711                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35712                       var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
35713                       if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
35714                         placement[1] = 'left';
35715                       } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
35716                         placement[1] = 'right';
35717                       }
35718                     } else {
35719                       var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
35720                       if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
35721                         placement[1] = 'top';
35722                       } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
35723                         placement[1] = 'bottom';
35724                       }
35725                     }
35726                   }
35727                 }
35728
35729                 switch (placement[0]) {
35730                   case 'top':
35731                     targetElemPos.top = hostElemPos.top - targetHeight;
35732                     break;
35733                   case 'bottom':
35734                     targetElemPos.top = hostElemPos.top + hostElemPos.height;
35735                     break;
35736                   case 'left':
35737                     targetElemPos.left = hostElemPos.left - targetWidth;
35738                     break;
35739                   case 'right':
35740                     targetElemPos.left = hostElemPos.left + hostElemPos.width;
35741                     break;
35742                 }
35743
35744                 switch (placement[1]) {
35745                   case 'top':
35746                     targetElemPos.top = hostElemPos.top;
35747                     break;
35748                   case 'bottom':
35749                     targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
35750                     break;
35751                   case 'left':
35752                     targetElemPos.left = hostElemPos.left;
35753                     break;
35754                   case 'right':
35755                     targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
35756                     break;
35757                   case 'center':
35758                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35759                       targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
35760                     } else {
35761                       targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
35762                     }
35763                     break;
35764                 }
35765
35766                 targetElemPos.top = Math.round(targetElemPos.top);
35767                 targetElemPos.left = Math.round(targetElemPos.left);
35768                 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
35769
35770                 return targetElemPos;
35771               },
35772
35773               /**
35774               * Provides a way for positioning tooltip & dropdown
35775               * arrows when using placement options beyond the standard
35776               * left, right, top, or bottom.
35777               *
35778               * @param {element} elem - The tooltip/dropdown element.
35779               * @param {string} placement - The placement for the elem.
35780               */
35781               positionArrow: function(elem, placement) {
35782                 elem = this.getRawNode(elem);
35783
35784                 var isTooltip = true;
35785
35786                 var innerElem = elem.querySelector('.tooltip-inner');
35787                 if (!innerElem) {
35788                   isTooltip = false;
35789                   innerElem = elem.querySelector('.popover-inner');
35790                 }
35791                 if (!innerElem) {
35792                   return;
35793                 }
35794
35795                 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
35796                 if (!arrowElem) {
35797                   return;
35798                 }
35799
35800                 placement = this.parsePlacement(placement);
35801                 if (placement[1] === 'center') {
35802                   // no adjustment necessary - just reset styles
35803                   angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
35804                   return;
35805                 }
35806
35807                 var borderProp = 'border-' + placement[0] + '-width';
35808                 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
35809
35810                 var borderRadiusProp = 'border-';
35811                 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35812                   borderRadiusProp += placement[0] + '-' + placement[1];
35813                 } else {
35814                   borderRadiusProp += placement[1] + '-' + placement[0];
35815                 }
35816                 borderRadiusProp += '-radius';
35817                 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
35818
35819                 var arrowCss = {
35820                   top: 'auto',
35821                   bottom: 'auto',
35822                   left: 'auto',
35823                   right: 'auto',
35824                   margin: 0
35825                 };
35826
35827                 switch (placement[0]) {
35828                   case 'top':
35829                     arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
35830                     break;
35831                   case 'bottom':
35832                     arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
35833                     break;
35834                   case 'left':
35835                     arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
35836                     break;
35837                   case 'right':
35838                     arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
35839                     break;
35840                 }
35841
35842                 arrowCss[placement[1]] = borderRadius;
35843
35844                 angular.element(arrowElem).css(arrowCss);
35845               }
35846             };
35847           }]);
35848
35849         angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position'])
35850
35851         .value('$datepickerSuppressError', false)
35852
35853         .constant('uibDatepickerConfig', {
35854           formatDay: 'dd',
35855           formatMonth: 'MMMM',
35856           formatYear: 'yyyy',
35857           formatDayHeader: 'EEE',
35858           formatDayTitle: 'MMMM yyyy',
35859           formatMonthTitle: 'yyyy',
35860           datepickerMode: 'day',
35861           minMode: 'day',
35862           maxMode: 'year',
35863           showWeeks: true,
35864           startingDay: 0,
35865           yearRows: 4,
35866           yearColumns: 5,
35867           minDate: null,
35868           maxDate: null,
35869           shortcutPropagation: false,
35870           ngModelOptions: {}
35871         })
35872
35873         .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser',
35874           function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) {
35875           var self = this,
35876               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
35877               ngModelOptions = {};
35878
35879           // Modes chain
35880           this.modes = ['day', 'month', 'year'];
35881
35882           // Interpolated configuration attributes
35883           angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) {
35884             self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key];
35885           });
35886
35887           // Evaled configuration attributes
35888           angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) {
35889             self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key];
35890           });
35891
35892           // Watchable date attributes
35893           angular.forEach(['minDate', 'maxDate'], function(key) {
35894             if ($attrs[key]) {
35895               $scope.$parent.$watch($attrs[key], function(value) {
35896                 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null;
35897                 self.refreshView();
35898               });
35899             } else {
35900               self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null;
35901             }
35902           });
35903
35904           angular.forEach(['minMode', 'maxMode'], function(key) {
35905             if ($attrs[key]) {
35906               $scope.$parent.$watch($attrs[key], function(value) {
35907                 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key];
35908                 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) ||
35909                   key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) {
35910                   $scope.datepickerMode = self[key];
35911                 }
35912               });
35913             } else {
35914               self[key] = $scope[key] = datepickerConfig[key] || null;
35915             }
35916           });
35917
35918           $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
35919           $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
35920
35921           if (angular.isDefined($attrs.initDate)) {
35922             this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date();
35923             $scope.$parent.$watch($attrs.initDate, function(initDate) {
35924               if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
35925                 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
35926                 self.refreshView();
35927               }
35928             });
35929           } else {
35930             this.activeDate = new Date();
35931           }
35932
35933           $scope.disabled = angular.isDefined($attrs.disabled) || false;
35934           if (angular.isDefined($attrs.ngDisabled)) {
35935             $scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
35936               $scope.disabled = disabled;
35937               self.refreshView();
35938             });
35939           }
35940
35941           $scope.isActive = function(dateObject) {
35942             if (self.compare(dateObject.date, self.activeDate) === 0) {
35943               $scope.activeDateId = dateObject.uid;
35944               return true;
35945             }
35946             return false;
35947           };
35948
35949           this.init = function(ngModelCtrl_) {
35950             ngModelCtrl = ngModelCtrl_;
35951             ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
35952
35953             if (ngModelCtrl.$modelValue) {
35954               this.activeDate = ngModelCtrl.$modelValue;
35955             }
35956
35957             ngModelCtrl.$render = function() {
35958               self.render();
35959             };
35960           };
35961
35962           this.render = function() {
35963             if (ngModelCtrl.$viewValue) {
35964               var date = new Date(ngModelCtrl.$viewValue),
35965                   isValid = !isNaN(date);
35966
35967               if (isValid) {
35968                 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
35969               } else if (!$datepickerSuppressError) {
35970                 $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.');
35971               }
35972             }
35973             this.refreshView();
35974           };
35975
35976           this.refreshView = function() {
35977             if (this.element) {
35978               $scope.selectedDt = null;
35979               this._refreshView();
35980               if ($scope.activeDt) {
35981                 $scope.activeDateId = $scope.activeDt.uid;
35982               }
35983
35984               var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35985               date = dateParser.fromTimezone(date, ngModelOptions.timezone);
35986               ngModelCtrl.$setValidity('dateDisabled', !date ||
35987                 this.element && !this.isDisabled(date));
35988             }
35989           };
35990
35991           this.createDateObject = function(date, format) {
35992             var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35993             model = dateParser.fromTimezone(model, ngModelOptions.timezone);
35994             var dt = {
35995               date: date,
35996               label: dateFilter(date, format),
35997               selected: model && this.compare(date, model) === 0,
35998               disabled: this.isDisabled(date),
35999               current: this.compare(date, new Date()) === 0,
36000               customClass: this.customClass(date) || null
36001             };
36002
36003             if (model && this.compare(date, model) === 0) {
36004               $scope.selectedDt = dt;
36005             }
36006
36007             if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
36008               $scope.activeDt = dt;
36009             }
36010
36011             return dt;
36012           };
36013
36014           this.isDisabled = function(date) {
36015             return $scope.disabled ||
36016               this.minDate && this.compare(date, this.minDate) < 0 ||
36017               this.maxDate && this.compare(date, this.maxDate) > 0 ||
36018               $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
36019           };
36020
36021           this.customClass = function(date) {
36022             return $scope.customClass({date: date, mode: $scope.datepickerMode});
36023           };
36024
36025           // Split array into smaller arrays
36026           this.split = function(arr, size) {
36027             var arrays = [];
36028             while (arr.length > 0) {
36029               arrays.push(arr.splice(0, size));
36030             }
36031             return arrays;
36032           };
36033
36034           $scope.select = function(date) {
36035             if ($scope.datepickerMode === self.minMode) {
36036               var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
36037               dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
36038               dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
36039               ngModelCtrl.$setViewValue(dt);
36040               ngModelCtrl.$render();
36041             } else {
36042               self.activeDate = date;
36043               $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
36044             }
36045           };
36046
36047           $scope.move = function(direction) {
36048             var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
36049                 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
36050             self.activeDate.setFullYear(year, month, 1);
36051             self.refreshView();
36052           };
36053
36054           $scope.toggleMode = function(direction) {
36055             direction = direction || 1;
36056
36057             if ($scope.datepickerMode === self.maxMode && direction === 1 ||
36058               $scope.datepickerMode === self.minMode && direction === -1) {
36059               return;
36060             }
36061
36062             $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
36063           };
36064
36065           // Key event mapper
36066           $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
36067
36068           var focusElement = function() {
36069             self.element[0].focus();
36070           };
36071
36072           // Listen for focus requests from popup directive
36073           $scope.$on('uib:datepicker.focus', focusElement);
36074
36075           $scope.keydown = function(evt) {
36076             var key = $scope.keys[evt.which];
36077
36078             if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
36079               return;
36080             }
36081
36082             evt.preventDefault();
36083             if (!self.shortcutPropagation) {
36084               evt.stopPropagation();
36085             }
36086
36087             if (key === 'enter' || key === 'space') {
36088               if (self.isDisabled(self.activeDate)) {
36089                 return; // do nothing
36090               }
36091               $scope.select(self.activeDate);
36092             } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
36093               $scope.toggleMode(key === 'up' ? 1 : -1);
36094             } else {
36095               self.handleKeyDown(key, evt);
36096               self.refreshView();
36097             }
36098           };
36099         }])
36100
36101         .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36102           var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
36103
36104           this.step = { months: 1 };
36105           this.element = $element;
36106           function getDaysInMonth(year, month) {
36107             return month === 1 && year % 4 === 0 &&
36108               (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
36109           }
36110
36111           this.init = function(ctrl) {
36112             angular.extend(ctrl, this);
36113             scope.showWeeks = ctrl.showWeeks;
36114             ctrl.refreshView();
36115           };
36116
36117           this.getDates = function(startDate, n) {
36118             var dates = new Array(n), current = new Date(startDate), i = 0, date;
36119             while (i < n) {
36120               date = new Date(current);
36121               dates[i++] = date;
36122               current.setDate(current.getDate() + 1);
36123             }
36124             return dates;
36125           };
36126
36127           this._refreshView = function() {
36128             var year = this.activeDate.getFullYear(),
36129               month = this.activeDate.getMonth(),
36130               firstDayOfMonth = new Date(this.activeDate);
36131
36132             firstDayOfMonth.setFullYear(year, month, 1);
36133
36134             var difference = this.startingDay - firstDayOfMonth.getDay(),
36135               numDisplayedFromPreviousMonth = difference > 0 ?
36136                 7 - difference : - difference,
36137               firstDate = new Date(firstDayOfMonth);
36138
36139             if (numDisplayedFromPreviousMonth > 0) {
36140               firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
36141             }
36142
36143             // 42 is the number of days on a six-week calendar
36144             var days = this.getDates(firstDate, 42);
36145             for (var i = 0; i < 42; i ++) {
36146               days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
36147                 secondary: days[i].getMonth() !== month,
36148                 uid: scope.uniqueId + '-' + i
36149               });
36150             }
36151
36152             scope.labels = new Array(7);
36153             for (var j = 0; j < 7; j++) {
36154               scope.labels[j] = {
36155                 abbr: dateFilter(days[j].date, this.formatDayHeader),
36156                 full: dateFilter(days[j].date, 'EEEE')
36157               };
36158             }
36159
36160             scope.title = dateFilter(this.activeDate, this.formatDayTitle);
36161             scope.rows = this.split(days, 7);
36162
36163             if (scope.showWeeks) {
36164               scope.weekNumbers = [];
36165               var thursdayIndex = (4 + 7 - this.startingDay) % 7,
36166                   numWeeks = scope.rows.length;
36167               for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
36168                 scope.weekNumbers.push(
36169                   getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
36170               }
36171             }
36172           };
36173
36174           this.compare = function(date1, date2) {
36175             var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
36176             var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36177             _date1.setFullYear(date1.getFullYear());
36178             _date2.setFullYear(date2.getFullYear());
36179             return _date1 - _date2;
36180           };
36181
36182           function getISO8601WeekNumber(date) {
36183             var checkDate = new Date(date);
36184             checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
36185             var time = checkDate.getTime();
36186             checkDate.setMonth(0); // Compare with Jan 1
36187             checkDate.setDate(1);
36188             return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
36189           }
36190
36191           this.handleKeyDown = function(key, evt) {
36192             var date = this.activeDate.getDate();
36193
36194             if (key === 'left') {
36195               date = date - 1;
36196             } else if (key === 'up') {
36197               date = date - 7;
36198             } else if (key === 'right') {
36199               date = date + 1;
36200             } else if (key === 'down') {
36201               date = date + 7;
36202             } else if (key === 'pageup' || key === 'pagedown') {
36203               var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
36204               this.activeDate.setMonth(month, 1);
36205               date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
36206             } else if (key === 'home') {
36207               date = 1;
36208             } else if (key === 'end') {
36209               date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
36210             }
36211             this.activeDate.setDate(date);
36212           };
36213         }])
36214
36215         .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36216           this.step = { years: 1 };
36217           this.element = $element;
36218
36219           this.init = function(ctrl) {
36220             angular.extend(ctrl, this);
36221             ctrl.refreshView();
36222           };
36223
36224           this._refreshView = function() {
36225             var months = new Array(12),
36226                 year = this.activeDate.getFullYear(),
36227                 date;
36228
36229             for (var i = 0; i < 12; i++) {
36230               date = new Date(this.activeDate);
36231               date.setFullYear(year, i, 1);
36232               months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
36233                 uid: scope.uniqueId + '-' + i
36234               });
36235             }
36236
36237             scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
36238             scope.rows = this.split(months, 3);
36239           };
36240
36241           this.compare = function(date1, date2) {
36242             var _date1 = new Date(date1.getFullYear(), date1.getMonth());
36243             var _date2 = new Date(date2.getFullYear(), date2.getMonth());
36244             _date1.setFullYear(date1.getFullYear());
36245             _date2.setFullYear(date2.getFullYear());
36246             return _date1 - _date2;
36247           };
36248
36249           this.handleKeyDown = function(key, evt) {
36250             var date = this.activeDate.getMonth();
36251
36252             if (key === 'left') {
36253               date = date - 1;
36254             } else if (key === 'up') {
36255               date = date - 3;
36256             } else if (key === 'right') {
36257               date = date + 1;
36258             } else if (key === 'down') {
36259               date = date + 3;
36260             } else if (key === 'pageup' || key === 'pagedown') {
36261               var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
36262               this.activeDate.setFullYear(year);
36263             } else if (key === 'home') {
36264               date = 0;
36265             } else if (key === 'end') {
36266               date = 11;
36267             }
36268             this.activeDate.setMonth(date);
36269           };
36270         }])
36271
36272         .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36273           var columns, range;
36274           this.element = $element;
36275
36276           function getStartingYear(year) {
36277             return parseInt((year - 1) / range, 10) * range + 1;
36278           }
36279
36280           this.yearpickerInit = function() {
36281             columns = this.yearColumns;
36282             range = this.yearRows * columns;
36283             this.step = { years: range };
36284           };
36285
36286           this._refreshView = function() {
36287             var years = new Array(range), date;
36288
36289             for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
36290               date = new Date(this.activeDate);
36291               date.setFullYear(start + i, 0, 1);
36292               years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
36293                 uid: scope.uniqueId + '-' + i
36294               });
36295             }
36296
36297             scope.title = [years[0].label, years[range - 1].label].join(' - ');
36298             scope.rows = this.split(years, columns);
36299             scope.columns = columns;
36300           };
36301
36302           this.compare = function(date1, date2) {
36303             return date1.getFullYear() - date2.getFullYear();
36304           };
36305
36306           this.handleKeyDown = function(key, evt) {
36307             var date = this.activeDate.getFullYear();
36308
36309             if (key === 'left') {
36310               date = date - 1;
36311             } else if (key === 'up') {
36312               date = date - columns;
36313             } else if (key === 'right') {
36314               date = date + 1;
36315             } else if (key === 'down') {
36316               date = date + columns;
36317             } else if (key === 'pageup' || key === 'pagedown') {
36318               date += (key === 'pageup' ? - 1 : 1) * range;
36319             } else if (key === 'home') {
36320               date = getStartingYear(this.activeDate.getFullYear());
36321             } else if (key === 'end') {
36322               date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
36323             }
36324             this.activeDate.setFullYear(date);
36325           };
36326         }])
36327
36328         .directive('uibDatepicker', function() {
36329           return {
36330             replace: true,
36331             templateUrl: function(element, attrs) {
36332               return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
36333             },
36334             scope: {
36335               datepickerMode: '=?',
36336               dateDisabled: '&',
36337               customClass: '&',
36338               shortcutPropagation: '&?'
36339             },
36340             require: ['uibDatepicker', '^ngModel'],
36341             controller: 'UibDatepickerController',
36342             controllerAs: 'datepicker',
36343             link: function(scope, element, attrs, ctrls) {
36344               var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
36345
36346               datepickerCtrl.init(ngModelCtrl);
36347             }
36348           };
36349         })
36350
36351         .directive('uibDaypicker', function() {
36352           return {
36353             replace: true,
36354             templateUrl: function(element, attrs) {
36355               return attrs.templateUrl || 'uib/template/datepicker/day.html';
36356             },
36357             require: ['^uibDatepicker', 'uibDaypicker'],
36358             controller: 'UibDaypickerController',
36359             link: function(scope, element, attrs, ctrls) {
36360               var datepickerCtrl = ctrls[0],
36361                 daypickerCtrl = ctrls[1];
36362
36363               daypickerCtrl.init(datepickerCtrl);
36364             }
36365           };
36366         })
36367
36368         .directive('uibMonthpicker', function() {
36369           return {
36370             replace: true,
36371             templateUrl: function(element, attrs) {
36372               return attrs.templateUrl || 'uib/template/datepicker/month.html';
36373             },
36374             require: ['^uibDatepicker', 'uibMonthpicker'],
36375             controller: 'UibMonthpickerController',
36376             link: function(scope, element, attrs, ctrls) {
36377               var datepickerCtrl = ctrls[0],
36378                 monthpickerCtrl = ctrls[1];
36379
36380               monthpickerCtrl.init(datepickerCtrl);
36381             }
36382           };
36383         })
36384
36385         .directive('uibYearpicker', function() {
36386           return {
36387             replace: true,
36388             templateUrl: function(element, attrs) {
36389               return attrs.templateUrl || 'uib/template/datepicker/year.html';
36390             },
36391             require: ['^uibDatepicker', 'uibYearpicker'],
36392             controller: 'UibYearpickerController',
36393             link: function(scope, element, attrs, ctrls) {
36394               var ctrl = ctrls[0];
36395               angular.extend(ctrl, ctrls[1]);
36396               ctrl.yearpickerInit();
36397
36398               ctrl.refreshView();
36399             }
36400           };
36401         })
36402
36403         .constant('uibDatepickerPopupConfig', {
36404           datepickerPopup: 'yyyy-MM-dd',
36405           datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html',
36406           datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
36407           html5Types: {
36408             date: 'yyyy-MM-dd',
36409             'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
36410             'month': 'yyyy-MM'
36411           },
36412           currentText: 'Today',
36413           clearText: 'Clear',
36414           closeText: 'Done',
36415           closeOnDateSelection: true,
36416           appendToBody: false,
36417           showButtonBar: true,
36418           onOpenFocus: true,
36419           altInputFormats: []
36420         })
36421
36422         .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig',
36423         function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) {
36424           var self = this;
36425           var cache = {},
36426             isHtml5DateInput = false;
36427           var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
36428             datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
36429             ngModel, ngModelOptions, $popup, altInputFormats;
36430
36431           scope.watchData = {};
36432
36433           this.init = function(_ngModel_) {
36434             ngModel = _ngModel_;
36435             ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions;
36436             closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
36437             appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
36438             onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
36439             datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
36440             datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
36441             altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
36442
36443             scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
36444
36445             if (datepickerPopupConfig.html5Types[attrs.type]) {
36446               dateFormat = datepickerPopupConfig.html5Types[attrs.type];
36447               isHtml5DateInput = true;
36448             } else {
36449               dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
36450               attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
36451                   var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
36452                   // Invalidate the $modelValue to ensure that formatters re-run
36453                   // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
36454                   if (newDateFormat !== dateFormat) {
36455                     dateFormat = newDateFormat;
36456                     ngModel.$modelValue = null;
36457
36458                     if (!dateFormat) {
36459                       throw new Error('uibDatepickerPopup must have a date format specified.');
36460                     }
36461                   }
36462               });
36463             }
36464
36465             if (!dateFormat) {
36466               throw new Error('uibDatepickerPopup must have a date format specified.');
36467             }
36468
36469             if (isHtml5DateInput && attrs.uibDatepickerPopup) {
36470               throw new Error('HTML5 date input types do not support custom formats.');
36471             }
36472
36473             // popup element used to display calendar
36474             popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
36475             scope.ngModelOptions = angular.copy(ngModelOptions);
36476             scope.ngModelOptions.timezone = null;
36477             popupEl.attr({
36478               'ng-model': 'date',
36479               'ng-model-options': 'ngModelOptions',
36480               'ng-change': 'dateSelection(date)',
36481               'template-url': datepickerPopupTemplateUrl
36482             });
36483
36484             // datepicker element
36485             datepickerEl = angular.element(popupEl.children()[0]);
36486             datepickerEl.attr('template-url', datepickerTemplateUrl);
36487
36488             if (isHtml5DateInput) {
36489               if (attrs.type === 'month') {
36490                 datepickerEl.attr('datepicker-mode', '"month"');
36491                 datepickerEl.attr('min-mode', 'month');
36492               }
36493             }
36494
36495             if (attrs.datepickerOptions) {
36496               var options = scope.$parent.$eval(attrs.datepickerOptions);
36497               if (options && options.initDate) {
36498                 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone);
36499                 datepickerEl.attr('init-date', 'initDate');
36500                 delete options.initDate;
36501               }
36502               angular.forEach(options, function(value, option) {
36503                 datepickerEl.attr(cameltoDash(option), value);
36504               });
36505             }
36506
36507             angular.forEach(['minMode', 'maxMode'], function(key) {
36508               if (attrs[key]) {
36509                 scope.$parent.$watch(function() { return attrs[key]; }, function(value) {
36510                   scope.watchData[key] = value;
36511                 });
36512                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36513               }
36514             });
36515
36516             angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) {
36517               if (attrs[key]) {
36518                 var getAttribute = $parse(attrs[key]);
36519                 var propConfig = {
36520                   get: function() {
36521                     return getAttribute(scope.$parent);
36522                   }
36523                 };
36524
36525                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36526
36527                 // Propagate changes from datepicker to outside
36528                 if (key === 'datepickerMode') {
36529                   var setAttribute = getAttribute.assign;
36530                   propConfig.set = function(v) {
36531                     setAttribute(scope.$parent, v);
36532                   };
36533                 }
36534
36535                 Object.defineProperty(scope.watchData, key, propConfig);
36536               }
36537             });
36538
36539             angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) {
36540               if (attrs[key]) {
36541                 var getAttribute = $parse(attrs[key]);
36542
36543                 scope.$parent.$watch(getAttribute, function(value) {
36544                   if (key === 'minDate' || key === 'maxDate') {
36545                     cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium'));
36546                   }
36547
36548                   scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
36549                 });
36550
36551                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36552               }
36553             });
36554
36555             if (attrs.dateDisabled) {
36556               datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
36557             }
36558
36559             angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) {
36560               if (angular.isDefined(attrs[key])) {
36561                 datepickerEl.attr(cameltoDash(key), attrs[key]);
36562               }
36563             });
36564
36565             if (attrs.customClass) {
36566               datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
36567             }
36568
36569             if (!isHtml5DateInput) {
36570               // Internal API to maintain the correct ng-invalid-[key] class
36571               ngModel.$$parserName = 'date';
36572               ngModel.$validators.date = validator;
36573               ngModel.$parsers.unshift(parseDate);
36574               ngModel.$formatters.push(function(value) {
36575                 if (ngModel.$isEmpty(value)) {
36576                   scope.date = value;
36577                   return value;
36578                 }
36579                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36580                 return dateFilter(scope.date, dateFormat);
36581               });
36582             } else {
36583               ngModel.$formatters.push(function(value) {
36584                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36585                 return value;
36586               });
36587             }
36588
36589             // Detect changes in the view from the text box
36590             ngModel.$viewChangeListeners.push(function() {
36591               scope.date = parseDateString(ngModel.$viewValue);
36592             });
36593
36594             element.bind('keydown', inputKeydownBind);
36595
36596             $popup = $compile(popupEl)(scope);
36597             // Prevent jQuery cache memory leak (template is now redundant after linking)
36598             popupEl.remove();
36599
36600             if (appendToBody) {
36601               $document.find('body').append($popup);
36602             } else {
36603               element.after($popup);
36604             }
36605
36606             scope.$on('$destroy', function() {
36607               if (scope.isOpen === true) {
36608                 if (!$rootScope.$$phase) {
36609                   scope.$apply(function() {
36610                     scope.isOpen = false;
36611                   });
36612                 }
36613               }
36614
36615               $popup.remove();
36616               element.unbind('keydown', inputKeydownBind);
36617               $document.unbind('click', documentClickBind);
36618             });
36619           };
36620
36621           scope.getText = function(key) {
36622             return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
36623           };
36624
36625           scope.isDisabled = function(date) {
36626             if (date === 'today') {
36627               date = new Date();
36628             }
36629
36630             return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 ||
36631               scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0;
36632           };
36633
36634           scope.compare = function(date1, date2) {
36635             return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36636           };
36637
36638           // Inner change
36639           scope.dateSelection = function(dt) {
36640             if (angular.isDefined(dt)) {
36641               scope.date = dt;
36642             }
36643             var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
36644             element.val(date);
36645             ngModel.$setViewValue(date);
36646
36647             if (closeOnDateSelection) {
36648               scope.isOpen = false;
36649               element[0].focus();
36650             }
36651           };
36652
36653           scope.keydown = function(evt) {
36654             if (evt.which === 27) {
36655               evt.stopPropagation();
36656               scope.isOpen = false;
36657               element[0].focus();
36658             }
36659           };
36660
36661           scope.select = function(date) {
36662             if (date === 'today') {
36663               var today = new Date();
36664               if (angular.isDate(scope.date)) {
36665                 date = new Date(scope.date);
36666                 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
36667               } else {
36668                 date = new Date(today.setHours(0, 0, 0, 0));
36669               }
36670             }
36671             scope.dateSelection(date);
36672           };
36673
36674           scope.close = function() {
36675             scope.isOpen = false;
36676             element[0].focus();
36677           };
36678
36679           scope.disabled = angular.isDefined(attrs.disabled) || false;
36680           if (attrs.ngDisabled) {
36681             scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) {
36682               scope.disabled = disabled;
36683             });
36684           }
36685
36686           scope.$watch('isOpen', function(value) {
36687             if (value) {
36688               if (!scope.disabled) {
36689                 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
36690                 scope.position.top = scope.position.top + element.prop('offsetHeight');
36691
36692                 $timeout(function() {
36693                   if (onOpenFocus) {
36694                     scope.$broadcast('uib:datepicker.focus');
36695                   }
36696                   $document.bind('click', documentClickBind);
36697                 }, 0, false);
36698               } else {
36699                 scope.isOpen = false;
36700               }
36701             } else {
36702               $document.unbind('click', documentClickBind);
36703             }
36704           });
36705
36706           function cameltoDash(string) {
36707             return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
36708           }
36709
36710           function parseDateString(viewValue) {
36711             var date = dateParser.parse(viewValue, dateFormat, scope.date);
36712             if (isNaN(date)) {
36713               for (var i = 0; i < altInputFormats.length; i++) {
36714                 date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
36715                 if (!isNaN(date)) {
36716                   return date;
36717                 }
36718               }
36719             }
36720             return date;
36721           }
36722
36723           function parseDate(viewValue) {
36724             if (angular.isNumber(viewValue)) {
36725               // presumably timestamp to date object
36726               viewValue = new Date(viewValue);
36727             }
36728
36729             if (!viewValue) {
36730               return null;
36731             }
36732
36733             if (angular.isDate(viewValue) && !isNaN(viewValue)) {
36734               return viewValue;
36735             }
36736
36737             if (angular.isString(viewValue)) {
36738               var date = parseDateString(viewValue);
36739               if (!isNaN(date)) {
36740                 return dateParser.toTimezone(date, ngModelOptions.timezone);
36741               }
36742             }
36743
36744             return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
36745           }
36746
36747           function validator(modelValue, viewValue) {
36748             var value = modelValue || viewValue;
36749
36750             if (!attrs.ngRequired && !value) {
36751               return true;
36752             }
36753
36754             if (angular.isNumber(value)) {
36755               value = new Date(value);
36756             }
36757
36758             if (!value) {
36759               return true;
36760             }
36761
36762             if (angular.isDate(value) && !isNaN(value)) {
36763               return true;
36764             }
36765
36766             if (angular.isString(value)) {
36767               return !isNaN(parseDateString(viewValue));
36768             }
36769
36770             return false;
36771           }
36772
36773           function documentClickBind(event) {
36774             if (!scope.isOpen && scope.disabled) {
36775               return;
36776             }
36777
36778             var popup = $popup[0];
36779             var dpContainsTarget = element[0].contains(event.target);
36780             // The popup node may not be an element node
36781             // In some browsers (IE) only element nodes have the 'contains' function
36782             var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
36783             if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
36784               scope.$apply(function() {
36785                 scope.isOpen = false;
36786               });
36787             }
36788           }
36789
36790           function inputKeydownBind(evt) {
36791             if (evt.which === 27 && scope.isOpen) {
36792               evt.preventDefault();
36793               evt.stopPropagation();
36794               scope.$apply(function() {
36795                 scope.isOpen = false;
36796               });
36797               element[0].focus();
36798             } else if (evt.which === 40 && !scope.isOpen) {
36799               evt.preventDefault();
36800               evt.stopPropagation();
36801               scope.$apply(function() {
36802                 scope.isOpen = true;
36803               });
36804             }
36805           }
36806         }])
36807
36808         .directive('uibDatepickerPopup', function() {
36809           return {
36810             require: ['ngModel', 'uibDatepickerPopup'],
36811             controller: 'UibDatepickerPopupController',
36812             scope: {
36813               isOpen: '=?',
36814               currentText: '@',
36815               clearText: '@',
36816               closeText: '@',
36817               dateDisabled: '&',
36818               customClass: '&'
36819             },
36820             link: function(scope, element, attrs, ctrls) {
36821               var ngModel = ctrls[0],
36822                 ctrl = ctrls[1];
36823
36824               ctrl.init(ngModel);
36825             }
36826           };
36827         })
36828
36829         .directive('uibDatepickerPopupWrap', function() {
36830           return {
36831             replace: true,
36832             transclude: true,
36833             templateUrl: function(element, attrs) {
36834               return attrs.templateUrl || 'uib/template/datepicker/popup.html';
36835             }
36836           };
36837         });
36838
36839         angular.module('ui.bootstrap.debounce', [])
36840         /**
36841          * A helper, internal service that debounces a function
36842          */
36843           .factory('$$debounce', ['$timeout', function($timeout) {
36844             return function(callback, debounceTime) {
36845               var timeoutPromise;
36846
36847               return function() {
36848                 var self = this;
36849                 var args = Array.prototype.slice.call(arguments);
36850                 if (timeoutPromise) {
36851                   $timeout.cancel(timeoutPromise);
36852                 }
36853
36854                 timeoutPromise = $timeout(function() {
36855                   callback.apply(self, args);
36856                 }, debounceTime);
36857               };
36858             };
36859           }]);
36860
36861         angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
36862
36863         .constant('uibDropdownConfig', {
36864           appendToOpenClass: 'uib-dropdown-open',
36865           openClass: 'open'
36866         })
36867
36868         .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
36869           var openScope = null;
36870
36871           this.open = function(dropdownScope) {
36872             if (!openScope) {
36873               $document.on('click', closeDropdown);
36874               $document.on('keydown', keybindFilter);
36875             }
36876
36877             if (openScope && openScope !== dropdownScope) {
36878               openScope.isOpen = false;
36879             }
36880
36881             openScope = dropdownScope;
36882           };
36883
36884           this.close = function(dropdownScope) {
36885             if (openScope === dropdownScope) {
36886               openScope = null;
36887               $document.off('click', closeDropdown);
36888               $document.off('keydown', keybindFilter);
36889             }
36890           };
36891
36892           var closeDropdown = function(evt) {
36893             // This method may still be called during the same mouse event that
36894             // unbound this event handler. So check openScope before proceeding.
36895             if (!openScope) { return; }
36896
36897             if (evt && openScope.getAutoClose() === 'disabled') { return; }
36898
36899             if (evt && evt.which === 3) { return; }
36900
36901             var toggleElement = openScope.getToggleElement();
36902             if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
36903               return;
36904             }
36905
36906             var dropdownElement = openScope.getDropdownElement();
36907             if (evt && openScope.getAutoClose() === 'outsideClick' &&
36908               dropdownElement && dropdownElement[0].contains(evt.target)) {
36909               return;
36910             }
36911
36912             openScope.isOpen = false;
36913
36914             if (!$rootScope.$$phase) {
36915               openScope.$apply();
36916             }
36917           };
36918
36919           var keybindFilter = function(evt) {
36920             if (evt.which === 27) {
36921               openScope.focusToggleElement();
36922               closeDropdown();
36923             } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
36924               evt.preventDefault();
36925               evt.stopPropagation();
36926               openScope.focusDropdownEntry(evt.which);
36927             }
36928           };
36929         }])
36930
36931         .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) {
36932           var self = this,
36933             scope = $scope.$new(), // create a child scope so we are not polluting original one
36934             templateScope,
36935             appendToOpenClass = dropdownConfig.appendToOpenClass,
36936             openClass = dropdownConfig.openClass,
36937             getIsOpen,
36938             setIsOpen = angular.noop,
36939             toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
36940             appendToBody = false,
36941             appendTo = null,
36942             keynavEnabled = false,
36943             selectedOption = null,
36944             body = $document.find('body');
36945
36946           $element.addClass('dropdown');
36947
36948           this.init = function() {
36949             if ($attrs.isOpen) {
36950               getIsOpen = $parse($attrs.isOpen);
36951               setIsOpen = getIsOpen.assign;
36952
36953               $scope.$watch(getIsOpen, function(value) {
36954                 scope.isOpen = !!value;
36955               });
36956             }
36957
36958             if (angular.isDefined($attrs.dropdownAppendTo)) {
36959               var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
36960               if (appendToEl) {
36961                 appendTo = angular.element(appendToEl);
36962               }
36963             }
36964
36965             appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
36966             keynavEnabled = angular.isDefined($attrs.keyboardNav);
36967
36968             if (appendToBody && !appendTo) {
36969               appendTo = body;
36970             }
36971
36972             if (appendTo && self.dropdownMenu) {
36973               appendTo.append(self.dropdownMenu);
36974               $element.on('$destroy', function handleDestroyEvent() {
36975                 self.dropdownMenu.remove();
36976               });
36977             }
36978           };
36979
36980           this.toggle = function(open) {
36981             return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
36982           };
36983
36984           // Allow other directives to watch status
36985           this.isOpen = function() {
36986             return scope.isOpen;
36987           };
36988
36989           scope.getToggleElement = function() {
36990             return self.toggleElement;
36991           };
36992
36993           scope.getAutoClose = function() {
36994             return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
36995           };
36996
36997           scope.getElement = function() {
36998             return $element;
36999           };
37000
37001           scope.isKeynavEnabled = function() {
37002             return keynavEnabled;
37003           };
37004
37005           scope.focusDropdownEntry = function(keyCode) {
37006             var elems = self.dropdownMenu ? //If append to body is used.
37007               angular.element(self.dropdownMenu).find('a') :
37008               $element.find('ul').eq(0).find('a');
37009
37010             switch (keyCode) {
37011               case 40: {
37012                 if (!angular.isNumber(self.selectedOption)) {
37013                   self.selectedOption = 0;
37014                 } else {
37015                   self.selectedOption = self.selectedOption === elems.length - 1 ?
37016                     self.selectedOption :
37017                     self.selectedOption + 1;
37018                 }
37019                 break;
37020               }
37021               case 38: {
37022                 if (!angular.isNumber(self.selectedOption)) {
37023                   self.selectedOption = elems.length - 1;
37024                 } else {
37025                   self.selectedOption = self.selectedOption === 0 ?
37026                     0 : self.selectedOption - 1;
37027                 }
37028                 break;
37029               }
37030             }
37031             elems[self.selectedOption].focus();
37032           };
37033
37034           scope.getDropdownElement = function() {
37035             return self.dropdownMenu;
37036           };
37037
37038           scope.focusToggleElement = function() {
37039             if (self.toggleElement) {
37040               self.toggleElement[0].focus();
37041             }
37042           };
37043
37044           scope.$watch('isOpen', function(isOpen, wasOpen) {
37045             if (appendTo && self.dropdownMenu) {
37046               var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
37047                 css,
37048                 rightalign;
37049
37050               css = {
37051                 top: pos.top + 'px',
37052                 display: isOpen ? 'block' : 'none'
37053               };
37054
37055               rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
37056               if (!rightalign) {
37057                 css.left = pos.left + 'px';
37058                 css.right = 'auto';
37059               } else {
37060                 css.left = 'auto';
37061                 css.right = window.innerWidth -
37062                   (pos.left + $element.prop('offsetWidth')) + 'px';
37063               }
37064
37065               // Need to adjust our positioning to be relative to the appendTo container
37066               // if it's not the body element
37067               if (!appendToBody) {
37068                 var appendOffset = $position.offset(appendTo);
37069
37070                 css.top = pos.top - appendOffset.top + 'px';
37071
37072                 if (!rightalign) {
37073                   css.left = pos.left - appendOffset.left + 'px';
37074                 } else {
37075                   css.right = window.innerWidth -
37076                     (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
37077                 }
37078               }
37079
37080               self.dropdownMenu.css(css);
37081             }
37082
37083             var openContainer = appendTo ? appendTo : $element;
37084
37085             $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
37086               if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
37087                 toggleInvoker($scope, { open: !!isOpen });
37088               }
37089             });
37090
37091             if (isOpen) {
37092               if (self.dropdownMenuTemplateUrl) {
37093                 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
37094                   templateScope = scope.$new();
37095                   $compile(tplContent.trim())(templateScope, function(dropdownElement) {
37096                     var newEl = dropdownElement;
37097                     self.dropdownMenu.replaceWith(newEl);
37098                     self.dropdownMenu = newEl;
37099                   });
37100                 });
37101               }
37102
37103               scope.focusToggleElement();
37104               uibDropdownService.open(scope);
37105             } else {
37106               if (self.dropdownMenuTemplateUrl) {
37107                 if (templateScope) {
37108                   templateScope.$destroy();
37109                 }
37110                 var newEl = angular.element('<ul class="dropdown-menu"></ul>');
37111                 self.dropdownMenu.replaceWith(newEl);
37112                 self.dropdownMenu = newEl;
37113               }
37114
37115               uibDropdownService.close(scope);
37116               self.selectedOption = null;
37117             }
37118
37119             if (angular.isFunction(setIsOpen)) {
37120               setIsOpen($scope, isOpen);
37121             }
37122           });
37123
37124           $scope.$on('$locationChangeSuccess', function() {
37125             if (scope.getAutoClose() !== 'disabled') {
37126               scope.isOpen = false;
37127             }
37128           });
37129         }])
37130
37131         .directive('uibDropdown', function() {
37132           return {
37133             controller: 'UibDropdownController',
37134             link: function(scope, element, attrs, dropdownCtrl) {
37135               dropdownCtrl.init();
37136             }
37137           };
37138         })
37139
37140         .directive('uibDropdownMenu', function() {
37141           return {
37142             restrict: 'A',
37143             require: '?^uibDropdown',
37144             link: function(scope, element, attrs, dropdownCtrl) {
37145               if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
37146                 return;
37147               }
37148
37149               element.addClass('dropdown-menu');
37150
37151               var tplUrl = attrs.templateUrl;
37152               if (tplUrl) {
37153                 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
37154               }
37155
37156               if (!dropdownCtrl.dropdownMenu) {
37157                 dropdownCtrl.dropdownMenu = element;
37158               }
37159             }
37160           };
37161         })
37162
37163         .directive('uibDropdownToggle', function() {
37164           return {
37165             require: '?^uibDropdown',
37166             link: function(scope, element, attrs, dropdownCtrl) {
37167               if (!dropdownCtrl) {
37168                 return;
37169               }
37170
37171               element.addClass('dropdown-toggle');
37172
37173               dropdownCtrl.toggleElement = element;
37174
37175               var toggleDropdown = function(event) {
37176                 event.preventDefault();
37177
37178                 if (!element.hasClass('disabled') && !attrs.disabled) {
37179                   scope.$apply(function() {
37180                     dropdownCtrl.toggle();
37181                   });
37182                 }
37183               };
37184
37185               element.bind('click', toggleDropdown);
37186
37187               // WAI-ARIA
37188               element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
37189               scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
37190                 element.attr('aria-expanded', !!isOpen);
37191               });
37192
37193               scope.$on('$destroy', function() {
37194                 element.unbind('click', toggleDropdown);
37195               });
37196             }
37197           };
37198         });
37199
37200         angular.module('ui.bootstrap.stackedMap', [])
37201         /**
37202          * A helper, internal data structure that acts as a map but also allows getting / removing
37203          * elements in the LIFO order
37204          */
37205           .factory('$$stackedMap', function() {
37206             return {
37207               createNew: function() {
37208                 var stack = [];
37209
37210                 return {
37211                   add: function(key, value) {
37212                     stack.push({
37213                       key: key,
37214                       value: value
37215                     });
37216                   },
37217                   get: function(key) {
37218                     for (var i = 0; i < stack.length; i++) {
37219                       if (key === stack[i].key) {
37220                         return stack[i];
37221                       }
37222                     }
37223                   },
37224                   keys: function() {
37225                     var keys = [];
37226                     for (var i = 0; i < stack.length; i++) {
37227                       keys.push(stack[i].key);
37228                     }
37229                     return keys;
37230                   },
37231                   top: function() {
37232                     return stack[stack.length - 1];
37233                   },
37234                   remove: function(key) {
37235                     var idx = -1;
37236                     for (var i = 0; i < stack.length; i++) {
37237                       if (key === stack[i].key) {
37238                         idx = i;
37239                         break;
37240                       }
37241                     }
37242                     return stack.splice(idx, 1)[0];
37243                   },
37244                   removeTop: function() {
37245                     return stack.splice(stack.length - 1, 1)[0];
37246                   },
37247                   length: function() {
37248                     return stack.length;
37249                   }
37250                 };
37251               }
37252             };
37253           });
37254         angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
37255         /**
37256          * A helper, internal data structure that stores all references attached to key
37257          */
37258           .factory('$$multiMap', function() {
37259             return {
37260               createNew: function() {
37261                 var map = {};
37262
37263                 return {
37264                   entries: function() {
37265                     return Object.keys(map).map(function(key) {
37266                       return {
37267                         key: key,
37268                         value: map[key]
37269                       };
37270                     });
37271                   },
37272                   get: function(key) {
37273                     return map[key];
37274                   },
37275                   hasKey: function(key) {
37276                     return !!map[key];
37277                   },
37278                   keys: function() {
37279                     return Object.keys(map);
37280                   },
37281                   put: function(key, value) {
37282                     if (!map[key]) {
37283                       map[key] = [];
37284                     }
37285
37286                     map[key].push(value);
37287                   },
37288                   remove: function(key, value) {
37289                     var values = map[key];
37290
37291                     if (!values) {
37292                       return;
37293                     }
37294
37295                     var idx = values.indexOf(value);
37296
37297                     if (idx !== -1) {
37298                       values.splice(idx, 1);
37299                     }
37300
37301                     if (!values.length) {
37302                       delete map[key];
37303                     }
37304                   }
37305                 };
37306               }
37307             };
37308           })
37309
37310         /**
37311          * Pluggable resolve mechanism for the modal resolve resolution
37312          * Supports UI Router's $resolve service
37313          */
37314           .provider('$uibResolve', function() {
37315             var resolve = this;
37316             this.resolver = null;
37317
37318             this.setResolver = function(resolver) {
37319               this.resolver = resolver;
37320             };
37321
37322             this.$get = ['$injector', '$q', function($injector, $q) {
37323               var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
37324               return {
37325                 resolve: function(invocables, locals, parent, self) {
37326                   if (resolver) {
37327                     return resolver.resolve(invocables, locals, parent, self);
37328                   }
37329
37330                   var promises = [];
37331
37332                   angular.forEach(invocables, function(value) {
37333                     if (angular.isFunction(value) || angular.isArray(value)) {
37334                       promises.push($q.resolve($injector.invoke(value)));
37335                     } else if (angular.isString(value)) {
37336                       promises.push($q.resolve($injector.get(value)));
37337                     } else {
37338                       promises.push($q.resolve(value));
37339                     }
37340                   });
37341
37342                   return $q.all(promises).then(function(resolves) {
37343                     var resolveObj = {};
37344                     var resolveIter = 0;
37345                     angular.forEach(invocables, function(value, key) {
37346                       resolveObj[key] = resolves[resolveIter++];
37347                     });
37348
37349                     return resolveObj;
37350                   });
37351                 }
37352               };
37353             }];
37354           })
37355
37356         /**
37357          * A helper directive for the $modal service. It creates a backdrop element.
37358          */
37359           .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack',
37360           function($animateCss, $injector, $modalStack) {
37361             return {
37362               replace: true,
37363               templateUrl: 'uib/template/modal/backdrop.html',
37364               compile: function(tElement, tAttrs) {
37365                 tElement.addClass(tAttrs.backdropClass);
37366                 return linkFn;
37367               }
37368             };
37369
37370             function linkFn(scope, element, attrs) {
37371               if (attrs.modalInClass) {
37372                 $animateCss(element, {
37373                   addClass: attrs.modalInClass
37374                 }).start();
37375
37376                 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37377                   var done = setIsAsync();
37378                   if (scope.modalOptions.animation) {
37379                     $animateCss(element, {
37380                       removeClass: attrs.modalInClass
37381                     }).start().then(done);
37382                   } else {
37383                     done();
37384                   }
37385                 });
37386               }
37387             }
37388           }])
37389
37390           .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
37391           function($modalStack, $q, $animate, $animateCss, $document) {
37392             return {
37393               scope: {
37394                 index: '@'
37395               },
37396               replace: true,
37397               transclude: true,
37398               templateUrl: function(tElement, tAttrs) {
37399                 return tAttrs.templateUrl || 'uib/template/modal/window.html';
37400               },
37401               link: function(scope, element, attrs) {
37402                 element.addClass(attrs.windowClass || '');
37403                 element.addClass(attrs.windowTopClass || '');
37404                 scope.size = attrs.size;
37405
37406                 scope.close = function(evt) {
37407                   var modal = $modalStack.getTop();
37408                   if (modal && modal.value.backdrop &&
37409                     modal.value.backdrop !== 'static' &&
37410                     evt.target === evt.currentTarget) {
37411                     evt.preventDefault();
37412                     evt.stopPropagation();
37413                     $modalStack.dismiss(modal.key, 'backdrop click');
37414                   }
37415                 };
37416
37417                 // moved from template to fix issue #2280
37418                 element.on('click', scope.close);
37419
37420                 // This property is only added to the scope for the purpose of detecting when this directive is rendered.
37421                 // We can detect that by using this property in the template associated with this directive and then use
37422                 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
37423                 scope.$isRendered = true;
37424
37425                 // Deferred object that will be resolved when this modal is render.
37426                 var modalRenderDeferObj = $q.defer();
37427                 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
37428                 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
37429                 attrs.$observe('modalRender', function(value) {
37430                   if (value === 'true') {
37431                     modalRenderDeferObj.resolve();
37432                   }
37433                 });
37434
37435                 modalRenderDeferObj.promise.then(function() {
37436                   var animationPromise = null;
37437
37438                   if (attrs.modalInClass) {
37439                     animationPromise = $animateCss(element, {
37440                       addClass: attrs.modalInClass
37441                     }).start();
37442
37443                     scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37444                       var done = setIsAsync();
37445                       if ($animateCss) {
37446                         $animateCss(element, {
37447                           removeClass: attrs.modalInClass
37448                         }).start().then(done);
37449                       } else {
37450                         $animate.removeClass(element, attrs.modalInClass).then(done);
37451                       }
37452                     });
37453                   }
37454
37455
37456                   $q.when(animationPromise).then(function() {
37457                     /**
37458                      * If something within the freshly-opened modal already has focus (perhaps via a
37459                      * directive that causes focus). then no need to try and focus anything.
37460                      */
37461                     if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
37462                       var inputWithAutofocus = element[0].querySelector('[autofocus]');
37463                       /**
37464                        * Auto-focusing of a freshly-opened modal element causes any child elements
37465                        * with the autofocus attribute to lose focus. This is an issue on touch
37466                        * based devices which will show and then hide the onscreen keyboard.
37467                        * Attempts to refocus the autofocus element via JavaScript will not reopen
37468                        * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
37469                        * the modal element if the modal does not contain an autofocus element.
37470                        */
37471                       if (inputWithAutofocus) {
37472                         inputWithAutofocus.focus();
37473                       } else {
37474                         element[0].focus();
37475                       }
37476                     }
37477                   });
37478
37479                   // Notify {@link $modalStack} that modal is rendered.
37480                   var modal = $modalStack.getTop();
37481                   if (modal) {
37482                     $modalStack.modalRendered(modal.key);
37483                   }
37484                 });
37485               }
37486             };
37487           }])
37488
37489           .directive('uibModalAnimationClass', function() {
37490             return {
37491               compile: function(tElement, tAttrs) {
37492                 if (tAttrs.modalAnimation) {
37493                   tElement.addClass(tAttrs.uibModalAnimationClass);
37494                 }
37495               }
37496             };
37497           })
37498
37499           .directive('uibModalTransclude', function() {
37500             return {
37501               link: function(scope, element, attrs, controller, transclude) {
37502                 transclude(scope.$parent, function(clone) {
37503                   element.empty();
37504                   element.append(clone);
37505                 });
37506               }
37507             };
37508           })
37509
37510           .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
37511             '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap',
37512             function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) {
37513               var OPENED_MODAL_CLASS = 'modal-open';
37514
37515               var backdropDomEl, backdropScope;
37516               var openedWindows = $$stackedMap.createNew();
37517               var openedClasses = $$multiMap.createNew();
37518               var $modalStack = {
37519                 NOW_CLOSING_EVENT: 'modal.stack.now-closing'
37520               };
37521
37522               //Modal focus behavior
37523               var focusableElementList;
37524               var focusIndex = 0;
37525               var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
37526                 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
37527                 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
37528
37529               function backdropIndex() {
37530                 var topBackdropIndex = -1;
37531                 var opened = openedWindows.keys();
37532                 for (var i = 0; i < opened.length; i++) {
37533                   if (openedWindows.get(opened[i]).value.backdrop) {
37534                     topBackdropIndex = i;
37535                   }
37536                 }
37537                 return topBackdropIndex;
37538               }
37539
37540               $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
37541                 if (backdropScope) {
37542                   backdropScope.index = newBackdropIndex;
37543                 }
37544               });
37545
37546               function removeModalWindow(modalInstance, elementToReceiveFocus) {
37547                 var modalWindow = openedWindows.get(modalInstance).value;
37548                 var appendToElement = modalWindow.appendTo;
37549
37550                 //clean up the stack
37551                 openedWindows.remove(modalInstance);
37552
37553                 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
37554                   var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
37555                   openedClasses.remove(modalBodyClass, modalInstance);
37556                   appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
37557                   toggleTopWindowClass(true);
37558                 });
37559                 checkRemoveBackdrop();
37560
37561                 //move focus to specified element if available, or else to body
37562                 if (elementToReceiveFocus && elementToReceiveFocus.focus) {
37563                   elementToReceiveFocus.focus();
37564                 } else if (appendToElement.focus) {
37565                   appendToElement.focus();
37566                 }
37567               }
37568
37569               // Add or remove "windowTopClass" from the top window in the stack
37570               function toggleTopWindowClass(toggleSwitch) {
37571                 var modalWindow;
37572
37573                 if (openedWindows.length() > 0) {
37574                   modalWindow = openedWindows.top().value;
37575                   modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
37576                 }
37577               }
37578
37579               function checkRemoveBackdrop() {
37580                 //remove backdrop if no longer needed
37581                 if (backdropDomEl && backdropIndex() === -1) {
37582                   var backdropScopeRef = backdropScope;
37583                   removeAfterAnimate(backdropDomEl, backdropScope, function() {
37584                     backdropScopeRef = null;
37585                   });
37586                   backdropDomEl = undefined;
37587                   backdropScope = undefined;
37588                 }
37589               }
37590
37591               function removeAfterAnimate(domEl, scope, done, closedDeferred) {
37592                 var asyncDeferred;
37593                 var asyncPromise = null;
37594                 var setIsAsync = function() {
37595                   if (!asyncDeferred) {
37596                     asyncDeferred = $q.defer();
37597                     asyncPromise = asyncDeferred.promise;
37598                   }
37599
37600                   return function asyncDone() {
37601                     asyncDeferred.resolve();
37602                   };
37603                 };
37604                 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
37605
37606                 // Note that it's intentional that asyncPromise might be null.
37607                 // That's when setIsAsync has not been called during the
37608                 // NOW_CLOSING_EVENT broadcast.
37609                 return $q.when(asyncPromise).then(afterAnimating);
37610
37611                 function afterAnimating() {
37612                   if (afterAnimating.done) {
37613                     return;
37614                   }
37615                   afterAnimating.done = true;
37616
37617                   $animateCss(domEl, {
37618                     event: 'leave'
37619                   }).start().then(function() {
37620                     domEl.remove();
37621                     if (closedDeferred) {
37622                       closedDeferred.resolve();
37623                     }
37624                   });
37625
37626                   scope.$destroy();
37627                   if (done) {
37628                     done();
37629                   }
37630                 }
37631               }
37632
37633               $document.on('keydown', keydownListener);
37634
37635               $rootScope.$on('$destroy', function() {
37636                 $document.off('keydown', keydownListener);
37637               });
37638
37639               function keydownListener(evt) {
37640                 if (evt.isDefaultPrevented()) {
37641                   return evt;
37642                 }
37643
37644                 var modal = openedWindows.top();
37645                 if (modal) {
37646                   switch (evt.which) {
37647                     case 27: {
37648                       if (modal.value.keyboard) {
37649                         evt.preventDefault();
37650                         $rootScope.$apply(function() {
37651                           $modalStack.dismiss(modal.key, 'escape key press');
37652                         });
37653                       }
37654                       break;
37655                     }
37656                     case 9: {
37657                       $modalStack.loadFocusElementList(modal);
37658                       var focusChanged = false;
37659                       if (evt.shiftKey) {
37660                         if ($modalStack.isFocusInFirstItem(evt)) {
37661                           focusChanged = $modalStack.focusLastFocusableElement();
37662                         }
37663                       } else {
37664                         if ($modalStack.isFocusInLastItem(evt)) {
37665                           focusChanged = $modalStack.focusFirstFocusableElement();
37666                         }
37667                       }
37668
37669                       if (focusChanged) {
37670                         evt.preventDefault();
37671                         evt.stopPropagation();
37672                       }
37673                       break;
37674                     }
37675                   }
37676                 }
37677               }
37678
37679               $modalStack.open = function(modalInstance, modal) {
37680                 var modalOpener = $document[0].activeElement,
37681                   modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
37682
37683                 toggleTopWindowClass(false);
37684
37685                 openedWindows.add(modalInstance, {
37686                   deferred: modal.deferred,
37687                   renderDeferred: modal.renderDeferred,
37688                   closedDeferred: modal.closedDeferred,
37689                   modalScope: modal.scope,
37690                   backdrop: modal.backdrop,
37691                   keyboard: modal.keyboard,
37692                   openedClass: modal.openedClass,
37693                   windowTopClass: modal.windowTopClass,
37694                   animation: modal.animation,
37695                   appendTo: modal.appendTo
37696                 });
37697
37698                 openedClasses.put(modalBodyClass, modalInstance);
37699
37700                 var appendToElement = modal.appendTo,
37701                     currBackdropIndex = backdropIndex();
37702
37703                 if (!appendToElement.length) {
37704                   throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
37705                 }
37706
37707                 if (currBackdropIndex >= 0 && !backdropDomEl) {
37708                   backdropScope = $rootScope.$new(true);
37709                   backdropScope.modalOptions = modal;
37710                   backdropScope.index = currBackdropIndex;
37711                   backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
37712                   backdropDomEl.attr('backdrop-class', modal.backdropClass);
37713                   if (modal.animation) {
37714                     backdropDomEl.attr('modal-animation', 'true');
37715                   }
37716                   $compile(backdropDomEl)(backdropScope);
37717                   $animate.enter(backdropDomEl, appendToElement);
37718                 }
37719
37720                 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
37721                 angularDomEl.attr({
37722                   'template-url': modal.windowTemplateUrl,
37723                   'window-class': modal.windowClass,
37724                   'window-top-class': modal.windowTopClass,
37725                   'size': modal.size,
37726                   'index': openedWindows.length() - 1,
37727                   'animate': 'animate'
37728                 }).html(modal.content);
37729                 if (modal.animation) {
37730                   angularDomEl.attr('modal-animation', 'true');
37731                 }
37732
37733                 $animate.enter(angularDomEl, appendToElement)
37734                   .then(function() {
37735                     $compile(angularDomEl)(modal.scope);
37736                     $animate.addClass(appendToElement, modalBodyClass);
37737                   });
37738
37739                 openedWindows.top().value.modalDomEl = angularDomEl;
37740                 openedWindows.top().value.modalOpener = modalOpener;
37741
37742                 $modalStack.clearFocusListCache();
37743               };
37744
37745               function broadcastClosing(modalWindow, resultOrReason, closing) {
37746                 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
37747               }
37748
37749               $modalStack.close = function(modalInstance, result) {
37750                 var modalWindow = openedWindows.get(modalInstance);
37751                 if (modalWindow && broadcastClosing(modalWindow, result, true)) {
37752                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37753                   modalWindow.value.deferred.resolve(result);
37754                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37755                   return true;
37756                 }
37757                 return !modalWindow;
37758               };
37759
37760               $modalStack.dismiss = function(modalInstance, reason) {
37761                 var modalWindow = openedWindows.get(modalInstance);
37762                 if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
37763                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37764                   modalWindow.value.deferred.reject(reason);
37765                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37766                   return true;
37767                 }
37768                 return !modalWindow;
37769               };
37770
37771               $modalStack.dismissAll = function(reason) {
37772                 var topModal = this.getTop();
37773                 while (topModal && this.dismiss(topModal.key, reason)) {
37774                   topModal = this.getTop();
37775                 }
37776               };
37777
37778               $modalStack.getTop = function() {
37779                 return openedWindows.top();
37780               };
37781
37782               $modalStack.modalRendered = function(modalInstance) {
37783                 var modalWindow = openedWindows.get(modalInstance);
37784                 if (modalWindow) {
37785                   modalWindow.value.renderDeferred.resolve();
37786                 }
37787               };
37788
37789               $modalStack.focusFirstFocusableElement = function() {
37790                 if (focusableElementList.length > 0) {
37791                   focusableElementList[0].focus();
37792                   return true;
37793                 }
37794                 return false;
37795               };
37796               $modalStack.focusLastFocusableElement = function() {
37797                 if (focusableElementList.length > 0) {
37798                   focusableElementList[focusableElementList.length - 1].focus();
37799                   return true;
37800                 }
37801                 return false;
37802               };
37803
37804               $modalStack.isFocusInFirstItem = function(evt) {
37805                 if (focusableElementList.length > 0) {
37806                   return (evt.target || evt.srcElement) === focusableElementList[0];
37807                 }
37808                 return false;
37809               };
37810
37811               $modalStack.isFocusInLastItem = function(evt) {
37812                 if (focusableElementList.length > 0) {
37813                   return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
37814                 }
37815                 return false;
37816               };
37817
37818               $modalStack.clearFocusListCache = function() {
37819                 focusableElementList = [];
37820                 focusIndex = 0;
37821               };
37822
37823               $modalStack.loadFocusElementList = function(modalWindow) {
37824                 if (focusableElementList === undefined || !focusableElementList.length) {
37825                   if (modalWindow) {
37826                     var modalDomE1 = modalWindow.value.modalDomEl;
37827                     if (modalDomE1 && modalDomE1.length) {
37828                       focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
37829                     }
37830                   }
37831                 }
37832               };
37833
37834               return $modalStack;
37835             }])
37836
37837           .provider('$uibModal', function() {
37838             var $modalProvider = {
37839               options: {
37840                 animation: true,
37841                 backdrop: true, //can also be false or 'static'
37842                 keyboard: true
37843               },
37844               $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
37845                 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
37846                   var $modal = {};
37847
37848                   function getTemplatePromise(options) {
37849                     return options.template ? $q.when(options.template) :
37850                       $templateRequest(angular.isFunction(options.templateUrl) ?
37851                         options.templateUrl() : options.templateUrl);
37852                   }
37853
37854                   var promiseChain = null;
37855                   $modal.getPromiseChain = function() {
37856                     return promiseChain;
37857                   };
37858
37859                   $modal.open = function(modalOptions) {
37860                     var modalResultDeferred = $q.defer();
37861                     var modalOpenedDeferred = $q.defer();
37862                     var modalClosedDeferred = $q.defer();
37863                     var modalRenderDeferred = $q.defer();
37864
37865                     //prepare an instance of a modal to be injected into controllers and returned to a caller
37866                     var modalInstance = {
37867                       result: modalResultDeferred.promise,
37868                       opened: modalOpenedDeferred.promise,
37869                       closed: modalClosedDeferred.promise,
37870                       rendered: modalRenderDeferred.promise,
37871                       close: function (result) {
37872                         return $modalStack.close(modalInstance, result);
37873                       },
37874                       dismiss: function (reason) {
37875                         return $modalStack.dismiss(modalInstance, reason);
37876                       }
37877                     };
37878
37879                     //merge and clean up options
37880                     modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
37881                     modalOptions.resolve = modalOptions.resolve || {};
37882                     modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
37883
37884                     //verify options
37885                     if (!modalOptions.template && !modalOptions.templateUrl) {
37886                       throw new Error('One of template or templateUrl options is required.');
37887                     }
37888
37889                     var templateAndResolvePromise =
37890                       $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
37891
37892                     function resolveWithTemplate() {
37893                       return templateAndResolvePromise;
37894                     }
37895
37896                     // Wait for the resolution of the existing promise chain.
37897                     // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
37898                     // Then add to $modalStack and resolve opened.
37899                     // Finally clean up the chain variable if no subsequent modal has overwritten it.
37900                     var samePromise;
37901                     samePromise = promiseChain = $q.all([promiseChain])
37902                       .then(resolveWithTemplate, resolveWithTemplate)
37903                       .then(function resolveSuccess(tplAndVars) {
37904                         var providedScope = modalOptions.scope || $rootScope;
37905
37906                         var modalScope = providedScope.$new();
37907                         modalScope.$close = modalInstance.close;
37908                         modalScope.$dismiss = modalInstance.dismiss;
37909
37910                         modalScope.$on('$destroy', function() {
37911                           if (!modalScope.$$uibDestructionScheduled) {
37912                             modalScope.$dismiss('$uibUnscheduledDestruction');
37913                           }
37914                         });
37915
37916                         var ctrlInstance, ctrlLocals = {};
37917
37918                         //controllers
37919                         if (modalOptions.controller) {
37920                           ctrlLocals.$scope = modalScope;
37921                           ctrlLocals.$uibModalInstance = modalInstance;
37922                           angular.forEach(tplAndVars[1], function(value, key) {
37923                             ctrlLocals[key] = value;
37924                           });
37925
37926                           ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
37927                           if (modalOptions.controllerAs) {
37928                             if (modalOptions.bindToController) {
37929                               ctrlInstance.$close = modalScope.$close;
37930                               ctrlInstance.$dismiss = modalScope.$dismiss;
37931                               angular.extend(ctrlInstance, providedScope);
37932                             }
37933
37934                             modalScope[modalOptions.controllerAs] = ctrlInstance;
37935                           }
37936                         }
37937
37938                         $modalStack.open(modalInstance, {
37939                           scope: modalScope,
37940                           deferred: modalResultDeferred,
37941                           renderDeferred: modalRenderDeferred,
37942                           closedDeferred: modalClosedDeferred,
37943                           content: tplAndVars[0],
37944                           animation: modalOptions.animation,
37945                           backdrop: modalOptions.backdrop,
37946                           keyboard: modalOptions.keyboard,
37947                           backdropClass: modalOptions.backdropClass,
37948                           windowTopClass: modalOptions.windowTopClass,
37949                           windowClass: modalOptions.windowClass,
37950                           windowTemplateUrl: modalOptions.windowTemplateUrl,
37951                           size: modalOptions.size,
37952                           openedClass: modalOptions.openedClass,
37953                           appendTo: modalOptions.appendTo
37954                         });
37955                         modalOpenedDeferred.resolve(true);
37956
37957                     }, function resolveError(reason) {
37958                       modalOpenedDeferred.reject(reason);
37959                       modalResultDeferred.reject(reason);
37960                     })['finally'](function() {
37961                       if (promiseChain === samePromise) {
37962                         promiseChain = null;
37963                       }
37964                     });
37965
37966                     return modalInstance;
37967                   };
37968
37969                   return $modal;
37970                 }
37971               ]
37972             };
37973
37974             return $modalProvider;
37975           });
37976
37977         angular.module('ui.bootstrap.paging', [])
37978         /**
37979          * Helper internal service for generating common controller code between the
37980          * pager and pagination components
37981          */
37982         .factory('uibPaging', ['$parse', function($parse) {
37983           return {
37984             create: function(ctrl, $scope, $attrs) {
37985               ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
37986               ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
37987
37988               ctrl.init = function(ngModelCtrl, config) {
37989                 ctrl.ngModelCtrl = ngModelCtrl;
37990                 ctrl.config = config;
37991
37992                 ngModelCtrl.$render = function() {
37993                   ctrl.render();
37994                 };
37995
37996                 if ($attrs.itemsPerPage) {
37997                   $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
37998                     ctrl.itemsPerPage = parseInt(value, 10);
37999                     $scope.totalPages = ctrl.calculateTotalPages();
38000                     ctrl.updatePage();
38001                   });
38002                 } else {
38003                   ctrl.itemsPerPage = config.itemsPerPage;
38004                 }
38005
38006                 $scope.$watch('totalItems', function(newTotal, oldTotal) {
38007                   if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
38008                     $scope.totalPages = ctrl.calculateTotalPages();
38009                     ctrl.updatePage();
38010                   }
38011                 });
38012               };
38013
38014               ctrl.calculateTotalPages = function() {
38015                 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
38016                 return Math.max(totalPages || 0, 1);
38017               };
38018
38019               ctrl.render = function() {
38020                 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
38021               };
38022
38023               $scope.selectPage = function(page, evt) {
38024                 if (evt) {
38025                   evt.preventDefault();
38026                 }
38027
38028                 var clickAllowed = !$scope.ngDisabled || !evt;
38029                 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
38030                   if (evt && evt.target) {
38031                     evt.target.blur();
38032                   }
38033                   ctrl.ngModelCtrl.$setViewValue(page);
38034                   ctrl.ngModelCtrl.$render();
38035                 }
38036               };
38037
38038               $scope.getText = function(key) {
38039                 return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
38040               };
38041
38042               $scope.noPrevious = function() {
38043                 return $scope.page === 1;
38044               };
38045
38046               $scope.noNext = function() {
38047                 return $scope.page === $scope.totalPages;
38048               };
38049
38050               ctrl.updatePage = function() {
38051                 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
38052
38053                 if ($scope.page > $scope.totalPages) {
38054                   $scope.selectPage($scope.totalPages);
38055                 } else {
38056                   ctrl.ngModelCtrl.$render();
38057                 }
38058               };
38059             }
38060           };
38061         }]);
38062
38063         angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
38064
38065         .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
38066           $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
38067
38068           uibPaging.create(this, $scope, $attrs);
38069         }])
38070
38071         .constant('uibPagerConfig', {
38072           itemsPerPage: 10,
38073           previousText: '« Previous',
38074           nextText: 'Next »',
38075           align: true
38076         })
38077
38078         .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
38079           return {
38080             scope: {
38081               totalItems: '=',
38082               previousText: '@',
38083               nextText: '@',
38084               ngDisabled: '='
38085             },
38086             require: ['uibPager', '?ngModel'],
38087             controller: 'UibPagerController',
38088             controllerAs: 'pager',
38089             templateUrl: function(element, attrs) {
38090               return attrs.templateUrl || 'uib/template/pager/pager.html';
38091             },
38092             replace: true,
38093             link: function(scope, element, attrs, ctrls) {
38094               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38095
38096               if (!ngModelCtrl) {
38097                 return; // do nothing if no ng-model
38098               }
38099
38100               paginationCtrl.init(ngModelCtrl, uibPagerConfig);
38101             }
38102           };
38103         }]);
38104
38105         angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
38106         .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
38107           var ctrl = this;
38108           // Setup configuration parameters
38109           var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
38110             rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
38111             forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
38112             boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
38113           $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
38114           $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
38115
38116           uibPaging.create(this, $scope, $attrs);
38117
38118           if ($attrs.maxSize) {
38119             $scope.$parent.$watch($parse($attrs.maxSize), function(value) {
38120               maxSize = parseInt(value, 10);
38121               ctrl.render();
38122             });
38123           }
38124
38125           // Create page object used in template
38126           function makePage(number, text, isActive) {
38127             return {
38128               number: number,
38129               text: text,
38130               active: isActive
38131             };
38132           }
38133
38134           function getPages(currentPage, totalPages) {
38135             var pages = [];
38136
38137             // Default page limits
38138             var startPage = 1, endPage = totalPages;
38139             var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
38140
38141             // recompute if maxSize
38142             if (isMaxSized) {
38143               if (rotate) {
38144                 // Current page is displayed in the middle of the visible ones
38145                 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
38146                 endPage = startPage + maxSize - 1;
38147
38148                 // Adjust if limit is exceeded
38149                 if (endPage > totalPages) {
38150                   endPage = totalPages;
38151                   startPage = endPage - maxSize + 1;
38152                 }
38153               } else {
38154                 // Visible pages are paginated with maxSize
38155                 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
38156
38157                 // Adjust last page if limit is exceeded
38158                 endPage = Math.min(startPage + maxSize - 1, totalPages);
38159               }
38160             }
38161
38162             // Add page number links
38163             for (var number = startPage; number <= endPage; number++) {
38164               var page = makePage(number, number, number === currentPage);
38165               pages.push(page);
38166             }
38167
38168             // Add links to move between page sets
38169             if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
38170               if (startPage > 1) {
38171                 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
38172                 var previousPageSet = makePage(startPage - 1, '...', false);
38173                 pages.unshift(previousPageSet);
38174               }
38175                 if (boundaryLinkNumbers) {
38176                   if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
38177                     var secondPageLink = makePage(2, '2', false);
38178                     pages.unshift(secondPageLink);
38179                   }
38180                   //add the first page
38181                   var firstPageLink = makePage(1, '1', false);
38182                   pages.unshift(firstPageLink);
38183                 }
38184               }
38185
38186               if (endPage < totalPages) {
38187                 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
38188                 var nextPageSet = makePage(endPage + 1, '...', false);
38189                 pages.push(nextPageSet);
38190               }
38191                 if (boundaryLinkNumbers) {
38192                   if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
38193                     var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
38194                     pages.push(secondToLastPageLink);
38195                   }
38196                   //add the last page
38197                   var lastPageLink = makePage(totalPages, totalPages, false);
38198                   pages.push(lastPageLink);
38199                 }
38200               }
38201             }
38202             return pages;
38203           }
38204
38205           var originalRender = this.render;
38206           this.render = function() {
38207             originalRender();
38208             if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
38209               $scope.pages = getPages($scope.page, $scope.totalPages);
38210             }
38211           };
38212         }])
38213
38214         .constant('uibPaginationConfig', {
38215           itemsPerPage: 10,
38216           boundaryLinks: false,
38217           boundaryLinkNumbers: false,
38218           directionLinks: true,
38219           firstText: 'First',
38220           previousText: 'Previous',
38221           nextText: 'Next',
38222           lastText: 'Last',
38223           rotate: true,
38224           forceEllipses: false
38225         })
38226
38227         .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
38228           return {
38229             scope: {
38230               totalItems: '=',
38231               firstText: '@',
38232               previousText: '@',
38233               nextText: '@',
38234               lastText: '@',
38235               ngDisabled:'='
38236             },
38237             require: ['uibPagination', '?ngModel'],
38238             controller: 'UibPaginationController',
38239             controllerAs: 'pagination',
38240             templateUrl: function(element, attrs) {
38241               return attrs.templateUrl || 'uib/template/pagination/pagination.html';
38242             },
38243             replace: true,
38244             link: function(scope, element, attrs, ctrls) {
38245               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38246
38247               if (!ngModelCtrl) {
38248                  return; // do nothing if no ng-model
38249               }
38250
38251               paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
38252             }
38253           };
38254         }]);
38255
38256         /**
38257          * The following features are still outstanding: animation as a
38258          * function, placement as a function, inside, support for more triggers than
38259          * just mouse enter/leave, html tooltips, and selector delegation.
38260          */
38261         angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
38262
38263         /**
38264          * The $tooltip service creates tooltip- and popover-like directives as well as
38265          * houses global options for them.
38266          */
38267         .provider('$uibTooltip', function() {
38268           // The default options tooltip and popover.
38269           var defaultOptions = {
38270             placement: 'top',
38271             placementClassPrefix: '',
38272             animation: true,
38273             popupDelay: 0,
38274             popupCloseDelay: 0,
38275             useContentExp: false
38276           };
38277
38278           // Default hide triggers for each show trigger
38279           var triggerMap = {
38280             'mouseenter': 'mouseleave',
38281             'click': 'click',
38282             'outsideClick': 'outsideClick',
38283             'focus': 'blur',
38284             'none': ''
38285           };
38286
38287           // The options specified to the provider globally.
38288           var globalOptions = {};
38289
38290           /**
38291            * `options({})` allows global configuration of all tooltips in the
38292            * application.
38293            *
38294            *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
38295            *     // place tooltips left instead of top by default
38296            *     $tooltipProvider.options( { placement: 'left' } );
38297            *   });
38298            */
38299                 this.options = function(value) {
38300                         angular.extend(globalOptions, value);
38301                 };
38302
38303           /**
38304            * This allows you to extend the set of trigger mappings available. E.g.:
38305            *
38306            *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
38307            */
38308           this.setTriggers = function setTriggers(triggers) {
38309             angular.extend(triggerMap, triggers);
38310           };
38311
38312           /**
38313            * This is a helper function for translating camel-case to snake_case.
38314            */
38315           function snake_case(name) {
38316             var regexp = /[A-Z]/g;
38317             var separator = '-';
38318             return name.replace(regexp, function(letter, pos) {
38319               return (pos ? separator : '') + letter.toLowerCase();
38320             });
38321           }
38322
38323           /**
38324            * Returns the actual instance of the $tooltip service.
38325            * TODO support multiple triggers
38326            */
38327           this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
38328             var openedTooltips = $$stackedMap.createNew();
38329             $document.on('keypress', keypressListener);
38330
38331             $rootScope.$on('$destroy', function() {
38332               $document.off('keypress', keypressListener);
38333             });
38334
38335             function keypressListener(e) {
38336               if (e.which === 27) {
38337                 var last = openedTooltips.top();
38338                 if (last) {
38339                   last.value.close();
38340                   openedTooltips.removeTop();
38341                   last = null;
38342                 }
38343               }
38344             }
38345
38346             return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
38347               options = angular.extend({}, defaultOptions, globalOptions, options);
38348
38349               /**
38350                * Returns an object of show and hide triggers.
38351                *
38352                * If a trigger is supplied,
38353                * it is used to show the tooltip; otherwise, it will use the `trigger`
38354                * option passed to the `$tooltipProvider.options` method; else it will
38355                * default to the trigger supplied to this directive factory.
38356                *
38357                * The hide trigger is based on the show trigger. If the `trigger` option
38358                * was passed to the `$tooltipProvider.options` method, it will use the
38359                * mapped trigger from `triggerMap` or the passed trigger if the map is
38360                * undefined; otherwise, it uses the `triggerMap` value of the show
38361                * trigger; else it will just use the show trigger.
38362                */
38363               function getTriggers(trigger) {
38364                 var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
38365                 var hide = show.map(function(trigger) {
38366                   return triggerMap[trigger] || trigger;
38367                 });
38368                 return {
38369                   show: show,
38370                   hide: hide
38371                 };
38372               }
38373
38374               var directiveName = snake_case(ttType);
38375
38376               var startSym = $interpolate.startSymbol();
38377               var endSym = $interpolate.endSymbol();
38378               var template =
38379                 '<div '+ directiveName + '-popup '+
38380                   'title="' + startSym + 'title' + endSym + '" '+
38381                   (options.useContentExp ?
38382                     'content-exp="contentExp()" ' :
38383                     'content="' + startSym + 'content' + endSym + '" ') +
38384                   'placement="' + startSym + 'placement' + endSym + '" '+
38385                   'popup-class="' + startSym + 'popupClass' + endSym + '" '+
38386                   'animation="animation" ' +
38387                   'is-open="isOpen"' +
38388                   'origin-scope="origScope" ' +
38389                   'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
38390                   '>' +
38391                 '</div>';
38392
38393               return {
38394                 compile: function(tElem, tAttrs) {
38395                   var tooltipLinker = $compile(template);
38396
38397                   return function link(scope, element, attrs, tooltipCtrl) {
38398                     var tooltip;
38399                     var tooltipLinkedScope;
38400                     var transitionTimeout;
38401                     var showTimeout;
38402                     var hideTimeout;
38403                     var positionTimeout;
38404                     var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
38405                     var triggers = getTriggers(undefined);
38406                     var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
38407                     var ttScope = scope.$new(true);
38408                     var repositionScheduled = false;
38409                     var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
38410                     var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
38411                     var observers = [];
38412
38413                     var positionTooltip = function() {
38414                       // check if tooltip exists and is not empty
38415                       if (!tooltip || !tooltip.html()) { return; }
38416
38417                       if (!positionTimeout) {
38418                         positionTimeout = $timeout(function() {
38419                           // Reset the positioning.
38420                           tooltip.css({ top: 0, left: 0 });
38421
38422                           // Now set the calculated positioning.
38423                           var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
38424                           tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' });
38425
38426                           // If the placement class is prefixed, still need
38427                           // to remove the TWBS standard class.
38428                           if (options.placementClassPrefix) {
38429                             tooltip.removeClass('top bottom left right');
38430                           }
38431
38432                           tooltip.removeClass(
38433                             options.placementClassPrefix + 'top ' +
38434                             options.placementClassPrefix + 'top-left ' +
38435                             options.placementClassPrefix + 'top-right ' +
38436                             options.placementClassPrefix + 'bottom ' +
38437                             options.placementClassPrefix + 'bottom-left ' +
38438                             options.placementClassPrefix + 'bottom-right ' +
38439                             options.placementClassPrefix + 'left ' +
38440                             options.placementClassPrefix + 'left-top ' +
38441                             options.placementClassPrefix + 'left-bottom ' +
38442                             options.placementClassPrefix + 'right ' +
38443                             options.placementClassPrefix + 'right-top ' +
38444                             options.placementClassPrefix + 'right-bottom');
38445
38446                           var placement = ttPosition.placement.split('-');
38447                           tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement);
38448                           $position.positionArrow(tooltip, ttPosition.placement);
38449
38450                           positionTimeout = null;
38451                         }, 0, false);
38452                       }
38453                     };
38454
38455                     // Set up the correct scope to allow transclusion later
38456                     ttScope.origScope = scope;
38457
38458                     // By default, the tooltip is not open.
38459                     // TODO add ability to start tooltip opened
38460                     ttScope.isOpen = false;
38461                     openedTooltips.add(ttScope, {
38462                       close: hide
38463                     });
38464
38465                     function toggleTooltipBind() {
38466                       if (!ttScope.isOpen) {
38467                         showTooltipBind();
38468                       } else {
38469                         hideTooltipBind();
38470                       }
38471                     }
38472
38473                     // Show the tooltip with delay if specified, otherwise show it immediately
38474                     function showTooltipBind() {
38475                       if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
38476                         return;
38477                       }
38478
38479                       cancelHide();
38480                       prepareTooltip();
38481
38482                       if (ttScope.popupDelay) {
38483                         // Do nothing if the tooltip was already scheduled to pop-up.
38484                         // This happens if show is triggered multiple times before any hide is triggered.
38485                         if (!showTimeout) {
38486                           showTimeout = $timeout(show, ttScope.popupDelay, false);
38487                         }
38488                       } else {
38489                         show();
38490                       }
38491                     }
38492
38493                     function hideTooltipBind() {
38494                       cancelShow();
38495
38496                       if (ttScope.popupCloseDelay) {
38497                         if (!hideTimeout) {
38498                           hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
38499                         }
38500                       } else {
38501                         hide();
38502                       }
38503                     }
38504
38505                     // Show the tooltip popup element.
38506                     function show() {
38507                       cancelShow();
38508                       cancelHide();
38509
38510                       // Don't show empty tooltips.
38511                       if (!ttScope.content) {
38512                         return angular.noop;
38513                       }
38514
38515                       createTooltip();
38516
38517                       // And show the tooltip.
38518                       ttScope.$evalAsync(function() {
38519                         ttScope.isOpen = true;
38520                         assignIsOpen(true);
38521                         positionTooltip();
38522                       });
38523                     }
38524
38525                     function cancelShow() {
38526                       if (showTimeout) {
38527                         $timeout.cancel(showTimeout);
38528                         showTimeout = null;
38529                       }
38530
38531                       if (positionTimeout) {
38532                         $timeout.cancel(positionTimeout);
38533                         positionTimeout = null;
38534                       }
38535                     }
38536
38537                     // Hide the tooltip popup element.
38538                     function hide() {
38539                       if (!ttScope) {
38540                         return;
38541                       }
38542
38543                       // First things first: we don't show it anymore.
38544                       ttScope.$evalAsync(function() {
38545                         ttScope.isOpen = false;
38546                         assignIsOpen(false);
38547                         // And now we remove it from the DOM. However, if we have animation, we
38548                         // need to wait for it to expire beforehand.
38549                         // FIXME: this is a placeholder for a port of the transitions library.
38550                         // The fade transition in TWBS is 150ms.
38551                         if (ttScope.animation) {
38552                           if (!transitionTimeout) {
38553                             transitionTimeout = $timeout(removeTooltip, 150, false);
38554                           }
38555                         } else {
38556                           removeTooltip();
38557                         }
38558                       });
38559                     }
38560
38561                     function cancelHide() {
38562                       if (hideTimeout) {
38563                         $timeout.cancel(hideTimeout);
38564                         hideTimeout = null;
38565                       }
38566                       if (transitionTimeout) {
38567                         $timeout.cancel(transitionTimeout);
38568                         transitionTimeout = null;
38569                       }
38570                     }
38571
38572                     function createTooltip() {
38573                       // There can only be one tooltip element per directive shown at once.
38574                       if (tooltip) {
38575                         return;
38576                       }
38577
38578                       tooltipLinkedScope = ttScope.$new();
38579                       tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
38580                         if (appendToBody) {
38581                           $document.find('body').append(tooltip);
38582                         } else {
38583                           element.after(tooltip);
38584                         }
38585                       });
38586
38587                       prepObservers();
38588                     }
38589
38590                     function removeTooltip() {
38591                       cancelShow();
38592                       cancelHide();
38593                       unregisterObservers();
38594
38595                       if (tooltip) {
38596                         tooltip.remove();
38597                         tooltip = null;
38598                       }
38599                       if (tooltipLinkedScope) {
38600                         tooltipLinkedScope.$destroy();
38601                         tooltipLinkedScope = null;
38602                       }
38603                     }
38604
38605                     /**
38606                      * Set the initial scope values. Once
38607                      * the tooltip is created, the observers
38608                      * will be added to keep things in sync.
38609                      */
38610                     function prepareTooltip() {
38611                       ttScope.title = attrs[prefix + 'Title'];
38612                       if (contentParse) {
38613                         ttScope.content = contentParse(scope);
38614                       } else {
38615                         ttScope.content = attrs[ttType];
38616                       }
38617
38618                       ttScope.popupClass = attrs[prefix + 'Class'];
38619                       ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
38620
38621                       var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
38622                       var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
38623                       ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
38624                       ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
38625                     }
38626
38627                     function assignIsOpen(isOpen) {
38628                       if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
38629                         isOpenParse.assign(scope, isOpen);
38630                       }
38631                     }
38632
38633                     ttScope.contentExp = function() {
38634                       return ttScope.content;
38635                     };
38636
38637                     /**
38638                      * Observe the relevant attributes.
38639                      */
38640                     attrs.$observe('disabled', function(val) {
38641                       if (val) {
38642                         cancelShow();
38643                       }
38644
38645                       if (val && ttScope.isOpen) {
38646                         hide();
38647                       }
38648                     });
38649
38650                     if (isOpenParse) {
38651                       scope.$watch(isOpenParse, function(val) {
38652                         if (ttScope && !val === ttScope.isOpen) {
38653                           toggleTooltipBind();
38654                         }
38655                       });
38656                     }
38657
38658                     function prepObservers() {
38659                       observers.length = 0;
38660
38661                       if (contentParse) {
38662                         observers.push(
38663                           scope.$watch(contentParse, function(val) {
38664                             ttScope.content = val;
38665                             if (!val && ttScope.isOpen) {
38666                               hide();
38667                             }
38668                           })
38669                         );
38670
38671                         observers.push(
38672                           tooltipLinkedScope.$watch(function() {
38673                             if (!repositionScheduled) {
38674                               repositionScheduled = true;
38675                               tooltipLinkedScope.$$postDigest(function() {
38676                                 repositionScheduled = false;
38677                                 if (ttScope && ttScope.isOpen) {
38678                                   positionTooltip();
38679                                 }
38680                               });
38681                             }
38682                           })
38683                         );
38684                       } else {
38685                         observers.push(
38686                           attrs.$observe(ttType, function(val) {
38687                             ttScope.content = val;
38688                             if (!val && ttScope.isOpen) {
38689                               hide();
38690                             } else {
38691                               positionTooltip();
38692                             }
38693                           })
38694                         );
38695                       }
38696
38697                       observers.push(
38698                         attrs.$observe(prefix + 'Title', function(val) {
38699                           ttScope.title = val;
38700                           if (ttScope.isOpen) {
38701                             positionTooltip();
38702                           }
38703                         })
38704                       );
38705
38706                       observers.push(
38707                         attrs.$observe(prefix + 'Placement', function(val) {
38708                           ttScope.placement = val ? val : options.placement;
38709                           if (ttScope.isOpen) {
38710                             positionTooltip();
38711                           }
38712                         })
38713                       );
38714                     }
38715
38716                     function unregisterObservers() {
38717                       if (observers.length) {
38718                         angular.forEach(observers, function(observer) {
38719                           observer();
38720                         });
38721                         observers.length = 0;
38722                       }
38723                     }
38724
38725                     // hide tooltips/popovers for outsideClick trigger
38726                     function bodyHideTooltipBind(e) {
38727                       if (!ttScope || !ttScope.isOpen || !tooltip) {
38728                         return;
38729                       }
38730                       // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
38731                       if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
38732                         hideTooltipBind();
38733                       }
38734                     }
38735
38736                     var unregisterTriggers = function() {
38737                       triggers.show.forEach(function(trigger) {
38738                         if (trigger === 'outsideClick') {
38739                           element.off('click', toggleTooltipBind);
38740                         } else {
38741                           element.off(trigger, showTooltipBind);
38742                           element.off(trigger, toggleTooltipBind);
38743                         }
38744                       });
38745                       triggers.hide.forEach(function(trigger) {
38746                         if (trigger === 'outsideClick') {
38747                           $document.off('click', bodyHideTooltipBind);
38748                         } else {
38749                           element.off(trigger, hideTooltipBind);
38750                         }
38751                       });
38752                     };
38753
38754                     function prepTriggers() {
38755                       var val = attrs[prefix + 'Trigger'];
38756                       unregisterTriggers();
38757
38758                       triggers = getTriggers(val);
38759
38760                       if (triggers.show !== 'none') {
38761                         triggers.show.forEach(function(trigger, idx) {
38762                           if (trigger === 'outsideClick') {
38763                             element.on('click', toggleTooltipBind);
38764                             $document.on('click', bodyHideTooltipBind);
38765                           } else if (trigger === triggers.hide[idx]) {
38766                             element.on(trigger, toggleTooltipBind);
38767                           } else if (trigger) {
38768                             element.on(trigger, showTooltipBind);
38769                             element.on(triggers.hide[idx], hideTooltipBind);
38770                           }
38771
38772                           element.on('keypress', function(e) {
38773                             if (e.which === 27) {
38774                               hideTooltipBind();
38775                             }
38776                           });
38777                         });
38778                       }
38779                     }
38780
38781                     prepTriggers();
38782
38783                     var animation = scope.$eval(attrs[prefix + 'Animation']);
38784                     ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
38785
38786                     var appendToBodyVal;
38787                     var appendKey = prefix + 'AppendToBody';
38788                     if (appendKey in attrs && attrs[appendKey] === undefined) {
38789                       appendToBodyVal = true;
38790                     } else {
38791                       appendToBodyVal = scope.$eval(attrs[appendKey]);
38792                     }
38793
38794                     appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
38795
38796                     // if a tooltip is attached to <body> we need to remove it on
38797                     // location change as its parent scope will probably not be destroyed
38798                     // by the change.
38799                     if (appendToBody) {
38800                       scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
38801                         if (ttScope.isOpen) {
38802                           hide();
38803                         }
38804                       });
38805                     }
38806
38807                     // Make sure tooltip is destroyed and removed.
38808                     scope.$on('$destroy', function onDestroyTooltip() {
38809                       unregisterTriggers();
38810                       removeTooltip();
38811                       openedTooltips.remove(ttScope);
38812                       ttScope = null;
38813                     });
38814                   };
38815                 }
38816               };
38817             };
38818           }];
38819         })
38820
38821         // This is mostly ngInclude code but with a custom scope
38822         .directive('uibTooltipTemplateTransclude', [
38823                  '$animate', '$sce', '$compile', '$templateRequest',
38824         function ($animate, $sce, $compile, $templateRequest) {
38825           return {
38826             link: function(scope, elem, attrs) {
38827               var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
38828
38829               var changeCounter = 0,
38830                 currentScope,
38831                 previousElement,
38832                 currentElement;
38833
38834               var cleanupLastIncludeContent = function() {
38835                 if (previousElement) {
38836                   previousElement.remove();
38837                   previousElement = null;
38838                 }
38839
38840                 if (currentScope) {
38841                   currentScope.$destroy();
38842                   currentScope = null;
38843                 }
38844
38845                 if (currentElement) {
38846                   $animate.leave(currentElement).then(function() {
38847                     previousElement = null;
38848                   });
38849                   previousElement = currentElement;
38850                   currentElement = null;
38851                 }
38852               };
38853
38854               scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
38855                 var thisChangeId = ++changeCounter;
38856
38857                 if (src) {
38858                   //set the 2nd param to true to ignore the template request error so that the inner
38859                   //contents and scope can be cleaned up.
38860                   $templateRequest(src, true).then(function(response) {
38861                     if (thisChangeId !== changeCounter) { return; }
38862                     var newScope = origScope.$new();
38863                     var template = response;
38864
38865                     var clone = $compile(template)(newScope, function(clone) {
38866                       cleanupLastIncludeContent();
38867                       $animate.enter(clone, elem);
38868                     });
38869
38870                     currentScope = newScope;
38871                     currentElement = clone;
38872
38873                     currentScope.$emit('$includeContentLoaded', src);
38874                   }, function() {
38875                     if (thisChangeId === changeCounter) {
38876                       cleanupLastIncludeContent();
38877                       scope.$emit('$includeContentError', src);
38878                     }
38879                   });
38880                   scope.$emit('$includeContentRequested', src);
38881                 } else {
38882                   cleanupLastIncludeContent();
38883                 }
38884               });
38885
38886               scope.$on('$destroy', cleanupLastIncludeContent);
38887             }
38888           };
38889         }])
38890
38891         /**
38892          * Note that it's intentional that these classes are *not* applied through $animate.
38893          * They must not be animated as they're expected to be present on the tooltip on
38894          * initialization.
38895          */
38896         .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
38897           return {
38898             restrict: 'A',
38899             link: function(scope, element, attrs) {
38900               // need to set the primary position so the
38901               // arrow has space during position measure.
38902               // tooltip.positionTooltip()
38903               if (scope.placement) {
38904                 // // There are no top-left etc... classes
38905                 // // in TWBS, so we need the primary position.
38906                 var position = $uibPosition.parsePlacement(scope.placement);
38907                 element.addClass(position[0]);
38908               } else {
38909                 element.addClass('top');
38910               }
38911
38912               if (scope.popupClass) {
38913                 element.addClass(scope.popupClass);
38914               }
38915
38916               if (scope.animation()) {
38917                 element.addClass(attrs.tooltipAnimationClass);
38918               }
38919             }
38920           };
38921         }])
38922
38923         .directive('uibTooltipPopup', function() {
38924           return {
38925             replace: true,
38926             scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38927             templateUrl: 'uib/template/tooltip/tooltip-popup.html'
38928           };
38929         })
38930
38931         .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
38932           return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
38933         }])
38934
38935         .directive('uibTooltipTemplatePopup', function() {
38936           return {
38937             replace: true,
38938             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38939               originScope: '&' },
38940             templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
38941           };
38942         })
38943
38944         .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
38945           return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
38946             useContentExp: true
38947           });
38948         }])
38949
38950         .directive('uibTooltipHtmlPopup', function() {
38951           return {
38952             replace: true,
38953             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38954             templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
38955           };
38956         })
38957
38958         .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
38959           return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
38960             useContentExp: true
38961           });
38962         }]);
38963
38964         /**
38965          * The following features are still outstanding: popup delay, animation as a
38966          * function, placement as a function, inside, support for more triggers than
38967          * just mouse enter/leave, and selector delegatation.
38968          */
38969         angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
38970
38971         .directive('uibPopoverTemplatePopup', function() {
38972           return {
38973             replace: true,
38974             scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38975               originScope: '&' },
38976             templateUrl: 'uib/template/popover/popover-template.html'
38977           };
38978         })
38979
38980         .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
38981           return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
38982             useContentExp: true
38983           });
38984         }])
38985
38986         .directive('uibPopoverHtmlPopup', function() {
38987           return {
38988             replace: true,
38989             scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38990             templateUrl: 'uib/template/popover/popover-html.html'
38991           };
38992         })
38993
38994         .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
38995           return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
38996             useContentExp: true
38997           });
38998         }])
38999
39000         .directive('uibPopoverPopup', function() {
39001           return {
39002             replace: true,
39003             scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
39004             templateUrl: 'uib/template/popover/popover.html'
39005           };
39006         })
39007
39008         .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
39009           return $uibTooltip('uibPopover', 'popover', 'click');
39010         }]);
39011
39012         angular.module('ui.bootstrap.progressbar', [])
39013
39014         .constant('uibProgressConfig', {
39015           animate: true,
39016           max: 100
39017         })
39018
39019         .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
39020           var self = this,
39021               animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
39022
39023           this.bars = [];
39024           $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
39025
39026           this.addBar = function(bar, element, attrs) {
39027             if (!animate) {
39028               element.css({'transition': 'none'});
39029             }
39030
39031             this.bars.push(bar);
39032
39033             bar.max = $scope.max;
39034             bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
39035
39036             bar.$watch('value', function(value) {
39037               bar.recalculatePercentage();
39038             });
39039
39040             bar.recalculatePercentage = function() {
39041               var totalPercentage = self.bars.reduce(function(total, bar) {
39042                 bar.percent = +(100 * bar.value / bar.max).toFixed(2);
39043                 return total + bar.percent;
39044               }, 0);
39045
39046               if (totalPercentage > 100) {
39047                 bar.percent -= totalPercentage - 100;
39048               }
39049             };
39050
39051             bar.$on('$destroy', function() {
39052               element = null;
39053               self.removeBar(bar);
39054             });
39055           };
39056
39057           this.removeBar = function(bar) {
39058             this.bars.splice(this.bars.indexOf(bar), 1);
39059             this.bars.forEach(function (bar) {
39060               bar.recalculatePercentage();
39061             });
39062           };
39063
39064           $scope.$watch('max', function(max) {
39065             self.bars.forEach(function(bar) {
39066               bar.max = $scope.max;
39067               bar.recalculatePercentage();
39068             });
39069           });
39070         }])
39071
39072         .directive('uibProgress', function() {
39073           return {
39074             replace: true,
39075             transclude: true,
39076             controller: 'UibProgressController',
39077             require: 'uibProgress',
39078             scope: {
39079               max: '=?'
39080             },
39081             templateUrl: 'uib/template/progressbar/progress.html'
39082           };
39083         })
39084
39085         .directive('uibBar', function() {
39086           return {
39087             replace: true,
39088             transclude: true,
39089             require: '^uibProgress',
39090             scope: {
39091               value: '=',
39092               type: '@'
39093             },
39094             templateUrl: 'uib/template/progressbar/bar.html',
39095             link: function(scope, element, attrs, progressCtrl) {
39096               progressCtrl.addBar(scope, element, attrs);
39097             }
39098           };
39099         })
39100
39101         .directive('uibProgressbar', function() {
39102           return {
39103             replace: true,
39104             transclude: true,
39105             controller: 'UibProgressController',
39106             scope: {
39107               value: '=',
39108               max: '=?',
39109               type: '@'
39110             },
39111             templateUrl: 'uib/template/progressbar/progressbar.html',
39112             link: function(scope, element, attrs, progressCtrl) {
39113               progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
39114             }
39115           };
39116         });
39117
39118         angular.module('ui.bootstrap.rating', [])
39119
39120         .constant('uibRatingConfig', {
39121           max: 5,
39122           stateOn: null,
39123           stateOff: null,
39124           titles : ['one', 'two', 'three', 'four', 'five']
39125         })
39126
39127         .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
39128           var ngModelCtrl = { $setViewValue: angular.noop };
39129
39130           this.init = function(ngModelCtrl_) {
39131             ngModelCtrl = ngModelCtrl_;
39132             ngModelCtrl.$render = this.render;
39133
39134             ngModelCtrl.$formatters.push(function(value) {
39135               if (angular.isNumber(value) && value << 0 !== value) {
39136                 value = Math.round(value);
39137               }
39138
39139               return value;
39140             });
39141
39142             this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
39143             this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
39144             var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
39145             this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
39146               tmpTitles : ratingConfig.titles;
39147
39148             var ratingStates = angular.isDefined($attrs.ratingStates) ?
39149               $scope.$parent.$eval($attrs.ratingStates) :
39150               new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
39151             $scope.range = this.buildTemplateObjects(ratingStates);
39152           };
39153
39154           this.buildTemplateObjects = function(states) {
39155             for (var i = 0, n = states.length; i < n; i++) {
39156               states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
39157             }
39158             return states;
39159           };
39160
39161           this.getTitle = function(index) {
39162             if (index >= this.titles.length) {
39163               return index + 1;
39164             }
39165
39166             return this.titles[index];
39167           };
39168
39169           $scope.rate = function(value) {
39170             if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
39171               ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
39172               ngModelCtrl.$render();
39173             }
39174           };
39175
39176           $scope.enter = function(value) {
39177             if (!$scope.readonly) {
39178               $scope.value = value;
39179             }
39180             $scope.onHover({value: value});
39181           };
39182
39183           $scope.reset = function() {
39184             $scope.value = ngModelCtrl.$viewValue;
39185             $scope.onLeave();
39186           };
39187
39188           $scope.onKeydown = function(evt) {
39189             if (/(37|38|39|40)/.test(evt.which)) {
39190               evt.preventDefault();
39191               evt.stopPropagation();
39192               $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
39193             }
39194           };
39195
39196           this.render = function() {
39197             $scope.value = ngModelCtrl.$viewValue;
39198           };
39199         }])
39200
39201         .directive('uibRating', function() {
39202           return {
39203             require: ['uibRating', 'ngModel'],
39204             scope: {
39205               readonly: '=?',
39206               onHover: '&',
39207               onLeave: '&'
39208             },
39209             controller: 'UibRatingController',
39210             templateUrl: 'uib/template/rating/rating.html',
39211             replace: true,
39212             link: function(scope, element, attrs, ctrls) {
39213               var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39214               ratingCtrl.init(ngModelCtrl);
39215             }
39216           };
39217         });
39218
39219         angular.module('ui.bootstrap.tabs', [])
39220
39221         .controller('UibTabsetController', ['$scope', function ($scope) {
39222           var ctrl = this,
39223               tabs = ctrl.tabs = $scope.tabs = [];
39224
39225           ctrl.select = function(selectedTab) {
39226             angular.forEach(tabs, function(tab) {
39227               if (tab.active && tab !== selectedTab) {
39228                 tab.active = false;
39229                 tab.onDeselect();
39230                 selectedTab.selectCalled = false;
39231               }
39232             });
39233             selectedTab.active = true;
39234             // only call select if it has not already been called
39235             if (!selectedTab.selectCalled) {
39236               selectedTab.onSelect();
39237               selectedTab.selectCalled = true;
39238             }
39239           };
39240
39241           ctrl.addTab = function addTab(tab) {
39242             tabs.push(tab);
39243             // we can't run the select function on the first tab
39244             // since that would select it twice
39245             if (tabs.length === 1 && tab.active !== false) {
39246               tab.active = true;
39247             } else if (tab.active) {
39248               ctrl.select(tab);
39249             } else {
39250               tab.active = false;
39251             }
39252           };
39253
39254           ctrl.removeTab = function removeTab(tab) {
39255             var index = tabs.indexOf(tab);
39256             //Select a new tab if the tab to be removed is selected and not destroyed
39257             if (tab.active && tabs.length > 1 && !destroyed) {
39258               //If this is the last tab, select the previous tab. else, the next tab.
39259               var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
39260               ctrl.select(tabs[newActiveIndex]);
39261             }
39262             tabs.splice(index, 1);
39263           };
39264
39265           var destroyed;
39266           $scope.$on('$destroy', function() {
39267             destroyed = true;
39268           });
39269         }])
39270
39271         .directive('uibTabset', function() {
39272           return {
39273             transclude: true,
39274             replace: true,
39275             scope: {
39276               type: '@'
39277             },
39278             controller: 'UibTabsetController',
39279             templateUrl: 'uib/template/tabs/tabset.html',
39280             link: function(scope, element, attrs) {
39281               scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
39282               scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
39283             }
39284           };
39285         })
39286
39287         .directive('uibTab', ['$parse', function($parse) {
39288           return {
39289             require: '^uibTabset',
39290             replace: true,
39291             templateUrl: 'uib/template/tabs/tab.html',
39292             transclude: true,
39293             scope: {
39294               active: '=?',
39295               heading: '@',
39296               onSelect: '&select', //This callback is called in contentHeadingTransclude
39297                                   //once it inserts the tab's content into the dom
39298               onDeselect: '&deselect'
39299             },
39300             controller: function() {
39301               //Empty controller so other directives can require being 'under' a tab
39302             },
39303             controllerAs: 'tab',
39304             link: function(scope, elm, attrs, tabsetCtrl, transclude) {
39305               scope.$watch('active', function(active) {
39306                 if (active) {
39307                   tabsetCtrl.select(scope);
39308                 }
39309               });
39310
39311               scope.disabled = false;
39312               if (attrs.disable) {
39313                 scope.$parent.$watch($parse(attrs.disable), function(value) {
39314                   scope.disabled = !! value;
39315                 });
39316               }
39317
39318               scope.select = function() {
39319                 if (!scope.disabled) {
39320                   scope.active = true;
39321                 }
39322               };
39323
39324               tabsetCtrl.addTab(scope);
39325               scope.$on('$destroy', function() {
39326                 tabsetCtrl.removeTab(scope);
39327               });
39328
39329               //We need to transclude later, once the content container is ready.
39330               //when this link happens, we're inside a tab heading.
39331               scope.$transcludeFn = transclude;
39332             }
39333           };
39334         }])
39335
39336         .directive('uibTabHeadingTransclude', function() {
39337           return {
39338             restrict: 'A',
39339             require: '^uibTab',
39340             link: function(scope, elm) {
39341               scope.$watch('headingElement', function updateHeadingElement(heading) {
39342                 if (heading) {
39343                   elm.html('');
39344                   elm.append(heading);
39345                 }
39346               });
39347             }
39348           };
39349         })
39350
39351         .directive('uibTabContentTransclude', function() {
39352           return {
39353             restrict: 'A',
39354             require: '^uibTabset',
39355             link: function(scope, elm, attrs) {
39356               var tab = scope.$eval(attrs.uibTabContentTransclude);
39357
39358               //Now our tab is ready to be transcluded: both the tab heading area
39359               //and the tab content area are loaded.  Transclude 'em both.
39360               tab.$transcludeFn(tab.$parent, function(contents) {
39361                 angular.forEach(contents, function(node) {
39362                   if (isTabHeading(node)) {
39363                     //Let tabHeadingTransclude know.
39364                     tab.headingElement = node;
39365                   } else {
39366                     elm.append(node);
39367                   }
39368                 });
39369               });
39370             }
39371           };
39372
39373           function isTabHeading(node) {
39374             return node.tagName && (
39375               node.hasAttribute('uib-tab-heading') ||
39376               node.hasAttribute('data-uib-tab-heading') ||
39377               node.hasAttribute('x-uib-tab-heading') ||
39378               node.tagName.toLowerCase() === 'uib-tab-heading' ||
39379               node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
39380               node.tagName.toLowerCase() === 'x-uib-tab-heading'
39381             );
39382           }
39383         });
39384
39385         angular.module('ui.bootstrap.timepicker', [])
39386
39387         .constant('uibTimepickerConfig', {
39388           hourStep: 1,
39389           minuteStep: 1,
39390           secondStep: 1,
39391           showMeridian: true,
39392           showSeconds: false,
39393           meridians: null,
39394           readonlyInput: false,
39395           mousewheel: true,
39396           arrowkeys: true,
39397           showSpinners: true
39398         })
39399
39400         .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
39401           var selected = new Date(),
39402               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
39403               meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
39404
39405           $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
39406           $element.removeAttr('tabindex');
39407
39408           this.init = function(ngModelCtrl_, inputs) {
39409             ngModelCtrl = ngModelCtrl_;
39410             ngModelCtrl.$render = this.render;
39411
39412             ngModelCtrl.$formatters.unshift(function(modelValue) {
39413               return modelValue ? new Date(modelValue) : null;
39414             });
39415
39416             var hoursInputEl = inputs.eq(0),
39417                 minutesInputEl = inputs.eq(1),
39418                 secondsInputEl = inputs.eq(2);
39419
39420             var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
39421
39422             if (mousewheel) {
39423               this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39424             }
39425
39426             var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
39427             if (arrowkeys) {
39428               this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39429             }
39430
39431             $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
39432             this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39433           };
39434
39435           var hourStep = timepickerConfig.hourStep;
39436           if ($attrs.hourStep) {
39437             $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
39438               hourStep = parseInt(value, 10);
39439             });
39440           }
39441
39442           var minuteStep = timepickerConfig.minuteStep;
39443           if ($attrs.minuteStep) {
39444             $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
39445               minuteStep = parseInt(value, 10);
39446             });
39447           }
39448
39449           var min;
39450           $scope.$parent.$watch($parse($attrs.min), function(value) {
39451             var dt = new Date(value);
39452             min = isNaN(dt) ? undefined : dt;
39453           });
39454
39455           var max;
39456           $scope.$parent.$watch($parse($attrs.max), function(value) {
39457             var dt = new Date(value);
39458             max = isNaN(dt) ? undefined : dt;
39459           });
39460
39461           var disabled = false;
39462           if ($attrs.ngDisabled) {
39463             $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
39464               disabled = value;
39465             });
39466           }
39467
39468           $scope.noIncrementHours = function() {
39469             var incrementedSelected = addMinutes(selected, hourStep * 60);
39470             return disabled || incrementedSelected > max ||
39471               incrementedSelected < selected && incrementedSelected < min;
39472           };
39473
39474           $scope.noDecrementHours = function() {
39475             var decrementedSelected = addMinutes(selected, -hourStep * 60);
39476             return disabled || decrementedSelected < min ||
39477               decrementedSelected > selected && decrementedSelected > max;
39478           };
39479
39480           $scope.noIncrementMinutes = function() {
39481             var incrementedSelected = addMinutes(selected, minuteStep);
39482             return disabled || incrementedSelected > max ||
39483               incrementedSelected < selected && incrementedSelected < min;
39484           };
39485
39486           $scope.noDecrementMinutes = function() {
39487             var decrementedSelected = addMinutes(selected, -minuteStep);
39488             return disabled || decrementedSelected < min ||
39489               decrementedSelected > selected && decrementedSelected > max;
39490           };
39491
39492           $scope.noIncrementSeconds = function() {
39493             var incrementedSelected = addSeconds(selected, secondStep);
39494             return disabled || incrementedSelected > max ||
39495               incrementedSelected < selected && incrementedSelected < min;
39496           };
39497
39498           $scope.noDecrementSeconds = function() {
39499             var decrementedSelected = addSeconds(selected, -secondStep);
39500             return disabled || decrementedSelected < min ||
39501               decrementedSelected > selected && decrementedSelected > max;
39502           };
39503
39504           $scope.noToggleMeridian = function() {
39505             if (selected.getHours() < 12) {
39506               return disabled || addMinutes(selected, 12 * 60) > max;
39507             }
39508
39509             return disabled || addMinutes(selected, -12 * 60) < min;
39510           };
39511
39512           var secondStep = timepickerConfig.secondStep;
39513           if ($attrs.secondStep) {
39514             $scope.$parent.$watch($parse($attrs.secondStep), function(value) {
39515               secondStep = parseInt(value, 10);
39516             });
39517           }
39518
39519           $scope.showSeconds = timepickerConfig.showSeconds;
39520           if ($attrs.showSeconds) {
39521             $scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
39522               $scope.showSeconds = !!value;
39523             });
39524           }
39525
39526           // 12H / 24H mode
39527           $scope.showMeridian = timepickerConfig.showMeridian;
39528           if ($attrs.showMeridian) {
39529             $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
39530               $scope.showMeridian = !!value;
39531
39532               if (ngModelCtrl.$error.time) {
39533                 // Evaluate from template
39534                 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
39535                 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39536                   selected.setHours(hours);
39537                   refresh();
39538                 }
39539               } else {
39540                 updateTemplate();
39541               }
39542             });
39543           }
39544
39545           // Get $scope.hours in 24H mode if valid
39546           function getHoursFromTemplate() {
39547             var hours = parseInt($scope.hours, 10);
39548             var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
39549               hours >= 0 && hours < 24;
39550             if (!valid) {
39551               return undefined;
39552             }
39553
39554             if ($scope.showMeridian) {
39555               if (hours === 12) {
39556                 hours = 0;
39557               }
39558               if ($scope.meridian === meridians[1]) {
39559                 hours = hours + 12;
39560               }
39561             }
39562             return hours;
39563           }
39564
39565           function getMinutesFromTemplate() {
39566             var minutes = parseInt($scope.minutes, 10);
39567             return minutes >= 0 && minutes < 60 ? minutes : undefined;
39568           }
39569
39570           function getSecondsFromTemplate() {
39571             var seconds = parseInt($scope.seconds, 10);
39572             return seconds >= 0 && seconds < 60 ? seconds : undefined;
39573           }
39574
39575           function pad(value) {
39576             if (value === null) {
39577               return '';
39578             }
39579
39580             return angular.isDefined(value) && value.toString().length < 2 ?
39581               '0' + value : value.toString();
39582           }
39583
39584           // Respond on mousewheel spin
39585           this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39586             var isScrollingUp = function(e) {
39587               if (e.originalEvent) {
39588                 e = e.originalEvent;
39589               }
39590               //pick correct delta variable depending on event
39591               var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
39592               return e.detail || delta > 0;
39593             };
39594
39595             hoursInputEl.bind('mousewheel wheel', function(e) {
39596               if (!disabled) {
39597                 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
39598               }
39599               e.preventDefault();
39600             });
39601
39602             minutesInputEl.bind('mousewheel wheel', function(e) {
39603               if (!disabled) {
39604                 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
39605               }
39606               e.preventDefault();
39607             });
39608
39609              secondsInputEl.bind('mousewheel wheel', function(e) {
39610               if (!disabled) {
39611                 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
39612               }
39613               e.preventDefault();
39614             });
39615           };
39616
39617           // Respond on up/down arrowkeys
39618           this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39619             hoursInputEl.bind('keydown', function(e) {
39620               if (!disabled) {
39621                 if (e.which === 38) { // up
39622                   e.preventDefault();
39623                   $scope.incrementHours();
39624                   $scope.$apply();
39625                 } else if (e.which === 40) { // down
39626                   e.preventDefault();
39627                   $scope.decrementHours();
39628                   $scope.$apply();
39629                 }
39630               }
39631             });
39632
39633             minutesInputEl.bind('keydown', function(e) {
39634               if (!disabled) {
39635                 if (e.which === 38) { // up
39636                   e.preventDefault();
39637                   $scope.incrementMinutes();
39638                   $scope.$apply();
39639                 } else if (e.which === 40) { // down
39640                   e.preventDefault();
39641                   $scope.decrementMinutes();
39642                   $scope.$apply();
39643                 }
39644               }
39645             });
39646
39647             secondsInputEl.bind('keydown', function(e) {
39648               if (!disabled) {
39649                 if (e.which === 38) { // up
39650                   e.preventDefault();
39651                   $scope.incrementSeconds();
39652                   $scope.$apply();
39653                 } else if (e.which === 40) { // down
39654                   e.preventDefault();
39655                   $scope.decrementSeconds();
39656                   $scope.$apply();
39657                 }
39658               }
39659             });
39660           };
39661
39662           this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39663             if ($scope.readonlyInput) {
39664               $scope.updateHours = angular.noop;
39665               $scope.updateMinutes = angular.noop;
39666               $scope.updateSeconds = angular.noop;
39667               return;
39668             }
39669
39670             var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
39671               ngModelCtrl.$setViewValue(null);
39672               ngModelCtrl.$setValidity('time', false);
39673               if (angular.isDefined(invalidHours)) {
39674                 $scope.invalidHours = invalidHours;
39675               }
39676
39677               if (angular.isDefined(invalidMinutes)) {
39678                 $scope.invalidMinutes = invalidMinutes;
39679               }
39680
39681               if (angular.isDefined(invalidSeconds)) {
39682                 $scope.invalidSeconds = invalidSeconds;
39683               }
39684             };
39685
39686             $scope.updateHours = function() {
39687               var hours = getHoursFromTemplate(),
39688                 minutes = getMinutesFromTemplate();
39689
39690               ngModelCtrl.$setDirty();
39691
39692               if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39693                 selected.setHours(hours);
39694                 selected.setMinutes(minutes);
39695                 if (selected < min || selected > max) {
39696                   invalidate(true);
39697                 } else {
39698                   refresh('h');
39699                 }
39700               } else {
39701                 invalidate(true);
39702               }
39703             };
39704
39705             hoursInputEl.bind('blur', function(e) {
39706               ngModelCtrl.$setTouched();
39707               if ($scope.hours === null || $scope.hours === '') {
39708                 invalidate(true);
39709               } else if (!$scope.invalidHours && $scope.hours < 10) {
39710                 $scope.$apply(function() {
39711                   $scope.hours = pad($scope.hours);
39712                 });
39713               }
39714             });
39715
39716             $scope.updateMinutes = function() {
39717               var minutes = getMinutesFromTemplate(),
39718                 hours = getHoursFromTemplate();
39719
39720               ngModelCtrl.$setDirty();
39721
39722               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39723                 selected.setHours(hours);
39724                 selected.setMinutes(minutes);
39725                 if (selected < min || selected > max) {
39726                   invalidate(undefined, true);
39727                 } else {
39728                   refresh('m');
39729                 }
39730               } else {
39731                 invalidate(undefined, true);
39732               }
39733             };
39734
39735             minutesInputEl.bind('blur', function(e) {
39736               ngModelCtrl.$setTouched();
39737               if ($scope.minutes === null) {
39738                 invalidate(undefined, true);
39739               } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
39740                 $scope.$apply(function() {
39741                   $scope.minutes = pad($scope.minutes);
39742                 });
39743               }
39744             });
39745
39746             $scope.updateSeconds = function() {
39747               var seconds = getSecondsFromTemplate();
39748
39749               ngModelCtrl.$setDirty();
39750
39751               if (angular.isDefined(seconds)) {
39752                 selected.setSeconds(seconds);
39753                 refresh('s');
39754               } else {
39755                 invalidate(undefined, undefined, true);
39756               }
39757             };
39758
39759             secondsInputEl.bind('blur', function(e) {
39760               if (!$scope.invalidSeconds && $scope.seconds < 10) {
39761                 $scope.$apply( function() {
39762                   $scope.seconds = pad($scope.seconds);
39763                 });
39764               }
39765             });
39766
39767           };
39768
39769           this.render = function() {
39770             var date = ngModelCtrl.$viewValue;
39771
39772             if (isNaN(date)) {
39773               ngModelCtrl.$setValidity('time', false);
39774               $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.');
39775             } else {
39776               if (date) {
39777                 selected = date;
39778               }
39779
39780               if (selected < min || selected > max) {
39781                 ngModelCtrl.$setValidity('time', false);
39782                 $scope.invalidHours = true;
39783                 $scope.invalidMinutes = true;
39784               } else {
39785                 makeValid();
39786               }
39787               updateTemplate();
39788             }
39789           };
39790
39791           // Call internally when we know that model is valid.
39792           function refresh(keyboardChange) {
39793             makeValid();
39794             ngModelCtrl.$setViewValue(new Date(selected));
39795             updateTemplate(keyboardChange);
39796           }
39797
39798           function makeValid() {
39799             ngModelCtrl.$setValidity('time', true);
39800             $scope.invalidHours = false;
39801             $scope.invalidMinutes = false;
39802             $scope.invalidSeconds = false;
39803           }
39804
39805           function updateTemplate(keyboardChange) {
39806             if (!ngModelCtrl.$modelValue) {
39807               $scope.hours = null;
39808               $scope.minutes = null;
39809               $scope.seconds = null;
39810               $scope.meridian = meridians[0];
39811             } else {
39812               var hours = selected.getHours(),
39813                 minutes = selected.getMinutes(),
39814                 seconds = selected.getSeconds();
39815
39816               if ($scope.showMeridian) {
39817                 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
39818               }
39819
39820               $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
39821               if (keyboardChange !== 'm') {
39822                 $scope.minutes = pad(minutes);
39823               }
39824               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39825
39826               if (keyboardChange !== 's') {
39827                 $scope.seconds = pad(seconds);
39828               }
39829               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39830             }
39831           }
39832
39833           function addSecondsToSelected(seconds) {
39834             selected = addSeconds(selected, seconds);
39835             refresh();
39836           }
39837
39838           function addMinutes(selected, minutes) {
39839             return addSeconds(selected, minutes*60);
39840           }
39841
39842           function addSeconds(date, seconds) {
39843             var dt = new Date(date.getTime() + seconds * 1000);
39844             var newDate = new Date(date);
39845             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
39846             return newDate;
39847           }
39848
39849           $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
39850             $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
39851
39852           $scope.incrementHours = function() {
39853             if (!$scope.noIncrementHours()) {
39854               addSecondsToSelected(hourStep * 60 * 60);
39855             }
39856           };
39857
39858           $scope.decrementHours = function() {
39859             if (!$scope.noDecrementHours()) {
39860               addSecondsToSelected(-hourStep * 60 * 60);
39861             }
39862           };
39863
39864           $scope.incrementMinutes = function() {
39865             if (!$scope.noIncrementMinutes()) {
39866               addSecondsToSelected(minuteStep * 60);
39867             }
39868           };
39869
39870           $scope.decrementMinutes = function() {
39871             if (!$scope.noDecrementMinutes()) {
39872               addSecondsToSelected(-minuteStep * 60);
39873             }
39874           };
39875
39876           $scope.incrementSeconds = function() {
39877             if (!$scope.noIncrementSeconds()) {
39878               addSecondsToSelected(secondStep);
39879             }
39880           };
39881
39882           $scope.decrementSeconds = function() {
39883             if (!$scope.noDecrementSeconds()) {
39884               addSecondsToSelected(-secondStep);
39885             }
39886           };
39887
39888           $scope.toggleMeridian = function() {
39889             var minutes = getMinutesFromTemplate(),
39890                 hours = getHoursFromTemplate();
39891
39892             if (!$scope.noToggleMeridian()) {
39893               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39894                 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
39895               } else {
39896                 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
39897               }
39898             }
39899           };
39900
39901           $scope.blur = function() {
39902             ngModelCtrl.$setTouched();
39903           };
39904         }])
39905
39906         .directive('uibTimepicker', function() {
39907           return {
39908             require: ['uibTimepicker', '?^ngModel'],
39909             controller: 'UibTimepickerController',
39910             controllerAs: 'timepicker',
39911             replace: true,
39912             scope: {},
39913             templateUrl: function(element, attrs) {
39914               return attrs.templateUrl || 'uib/template/timepicker/timepicker.html';
39915             },
39916             link: function(scope, element, attrs, ctrls) {
39917               var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39918
39919               if (ngModelCtrl) {
39920                 timepickerCtrl.init(ngModelCtrl, element.find('input'));
39921               }
39922             }
39923           };
39924         });
39925
39926         angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
39927
39928         /**
39929          * A helper service that can parse typeahead's syntax (string provided by users)
39930          * Extracted to a separate service for ease of unit testing
39931          */
39932           .factory('uibTypeaheadParser', ['$parse', function($parse) {
39933             //                      00000111000000000000022200000000000000003333333333333330000000000044000
39934             var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
39935             return {
39936               parse: function(input) {
39937                 var match = input.match(TYPEAHEAD_REGEXP);
39938                 if (!match) {
39939                   throw new Error(
39940                     'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
39941                       ' but got "' + input + '".');
39942                 }
39943
39944                 return {
39945                   itemName: match[3],
39946                   source: $parse(match[4]),
39947                   viewMapper: $parse(match[2] || match[1]),
39948                   modelMapper: $parse(match[1])
39949                 };
39950               }
39951             };
39952           }])
39953
39954           .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
39955             function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
39956             var HOT_KEYS = [9, 13, 27, 38, 40];
39957             var eventDebounceTime = 200;
39958             var modelCtrl, ngModelOptions;
39959             //SUPPORTED ATTRIBUTES (OPTIONS)
39960
39961             //minimal no of characters that needs to be entered before typeahead kicks-in
39962             var minLength = originalScope.$eval(attrs.typeaheadMinLength);
39963             if (!minLength && minLength !== 0) {
39964               minLength = 1;
39965             }
39966
39967             //minimal wait time after last character typed before typeahead kicks-in
39968             var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
39969
39970             //should it restrict model values to the ones selected from the popup only?
39971             var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
39972             originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
39973               isEditable = newVal !== false;
39974             });
39975
39976             //binding to a variable that indicates if matches are being retrieved asynchronously
39977             var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
39978
39979             //a callback executed when a match is selected
39980             var onSelectCallback = $parse(attrs.typeaheadOnSelect);
39981
39982             //should it select highlighted popup value when losing focus?
39983             var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
39984
39985             //binding to a variable that indicates if there were no results after the query is completed
39986             var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
39987
39988             var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
39989
39990             var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
39991
39992             var appendTo = attrs.typeaheadAppendTo ?
39993               originalScope.$eval(attrs.typeaheadAppendTo) : null;
39994
39995             var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
39996
39997             //If input matches an item of the list exactly, select it automatically
39998             var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
39999
40000             //binding to a variable that indicates if dropdown is open
40001             var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
40002
40003             var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
40004
40005             //INTERNAL VARIABLES
40006
40007             //model setter executed upon match selection
40008             var parsedModel = $parse(attrs.ngModel);
40009             var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
40010             var $setModelValue = function(scope, newValue) {
40011               if (angular.isFunction(parsedModel(originalScope)) &&
40012                 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
40013                 return invokeModelSetter(scope, {$$$p: newValue});
40014               }
40015
40016               return parsedModel.assign(scope, newValue);
40017             };
40018
40019             //expressions used by typeahead
40020             var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
40021
40022             var hasFocus;
40023
40024             //Used to avoid bug in iOS webview where iOS keyboard does not fire
40025             //mousedown & mouseup events
40026             //Issue #3699
40027             var selected;
40028
40029             //create a child scope for the typeahead directive so we are not polluting original scope
40030             //with typeahead-specific data (matches, query etc.)
40031             var scope = originalScope.$new();
40032             var offDestroy = originalScope.$on('$destroy', function() {
40033               scope.$destroy();
40034             });
40035             scope.$on('$destroy', offDestroy);
40036
40037             // WAI-ARIA
40038             var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
40039             element.attr({
40040               'aria-autocomplete': 'list',
40041               'aria-expanded': false,
40042               'aria-owns': popupId
40043             });
40044
40045             var inputsContainer, hintInputElem;
40046             //add read-only input to show hint
40047             if (showHint) {
40048               inputsContainer = angular.element('<div></div>');
40049               inputsContainer.css('position', 'relative');
40050               element.after(inputsContainer);
40051               hintInputElem = element.clone();
40052               hintInputElem.attr('placeholder', '');
40053               hintInputElem.val('');
40054               hintInputElem.css({
40055                 'position': 'absolute',
40056                 'top': '0px',
40057                 'left': '0px',
40058                 'border-color': 'transparent',
40059                 'box-shadow': 'none',
40060                 'opacity': 1,
40061                 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
40062                 'color': '#999'
40063               });
40064               element.css({
40065                 'position': 'relative',
40066                 'vertical-align': 'top',
40067                 'background-color': 'transparent'
40068               });
40069               inputsContainer.append(hintInputElem);
40070               hintInputElem.after(element);
40071             }
40072
40073             //pop-up element used to display matches
40074             var popUpEl = angular.element('<div uib-typeahead-popup></div>');
40075             popUpEl.attr({
40076               id: popupId,
40077               matches: 'matches',
40078               active: 'activeIdx',
40079               select: 'select(activeIdx, evt)',
40080               'move-in-progress': 'moveInProgress',
40081               query: 'query',
40082               position: 'position',
40083               'assign-is-open': 'assignIsOpen(isOpen)',
40084               debounce: 'debounceUpdate'
40085             });
40086             //custom item template
40087             if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
40088               popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
40089             }
40090
40091             if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
40092               popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
40093             }
40094
40095             var resetHint = function() {
40096               if (showHint) {
40097                 hintInputElem.val('');
40098               }
40099             };
40100
40101             var resetMatches = function() {
40102               scope.matches = [];
40103               scope.activeIdx = -1;
40104               element.attr('aria-expanded', false);
40105               resetHint();
40106             };
40107
40108             var getMatchId = function(index) {
40109               return popupId + '-option-' + index;
40110             };
40111
40112             // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
40113             // This attribute is added or removed automatically when the `activeIdx` changes.
40114             scope.$watch('activeIdx', function(index) {
40115               if (index < 0) {
40116                 element.removeAttr('aria-activedescendant');
40117               } else {
40118                 element.attr('aria-activedescendant', getMatchId(index));
40119               }
40120             });
40121
40122             var inputIsExactMatch = function(inputValue, index) {
40123               if (scope.matches.length > index && inputValue) {
40124                 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
40125               }
40126
40127               return false;
40128             };
40129
40130             var getMatchesAsync = function(inputValue, evt) {
40131               var locals = {$viewValue: inputValue};
40132               isLoadingSetter(originalScope, true);
40133               isNoResultsSetter(originalScope, false);
40134               $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
40135                 //it might happen that several async queries were in progress if a user were typing fast
40136                 //but we are interested only in responses that correspond to the current view value
40137                 var onCurrentRequest = inputValue === modelCtrl.$viewValue;
40138                 if (onCurrentRequest && hasFocus) {
40139                   if (matches && matches.length > 0) {
40140                     scope.activeIdx = focusFirst ? 0 : -1;
40141                     isNoResultsSetter(originalScope, false);
40142                     scope.matches.length = 0;
40143
40144                     //transform labels
40145                     for (var i = 0; i < matches.length; i++) {
40146                       locals[parserResult.itemName] = matches[i];
40147                       scope.matches.push({
40148                         id: getMatchId(i),
40149                         label: parserResult.viewMapper(scope, locals),
40150                         model: matches[i]
40151                       });
40152                     }
40153
40154                     scope.query = inputValue;
40155                     //position pop-up with matches - we need to re-calculate its position each time we are opening a window
40156                     //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
40157                     //due to other elements being rendered
40158                     recalculatePosition();
40159
40160                     element.attr('aria-expanded', true);
40161
40162                     //Select the single remaining option if user input matches
40163                     if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
40164                       if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40165                         $$debounce(function() {
40166                           scope.select(0, evt);
40167                         }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40168                       } else {
40169                         scope.select(0, evt);
40170                       }
40171                     }
40172
40173                     if (showHint) {
40174                       var firstLabel = scope.matches[0].label;
40175                       if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
40176                         hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
40177                       }
40178                       else {
40179                         hintInputElem.val('');
40180                       }
40181                     }
40182                   } else {
40183                     resetMatches();
40184                     isNoResultsSetter(originalScope, true);
40185                   }
40186                 }
40187                 if (onCurrentRequest) {
40188                   isLoadingSetter(originalScope, false);
40189                 }
40190               }, function() {
40191                 resetMatches();
40192                 isLoadingSetter(originalScope, false);
40193                 isNoResultsSetter(originalScope, true);
40194               });
40195             };
40196
40197             // bind events only if appendToBody params exist - performance feature
40198             if (appendToBody) {
40199               angular.element($window).on('resize', fireRecalculating);
40200               $document.find('body').on('scroll', fireRecalculating);
40201             }
40202
40203             // Declare the debounced function outside recalculating for
40204             // proper debouncing
40205             var debouncedRecalculate = $$debounce(function() {
40206               // if popup is visible
40207               if (scope.matches.length) {
40208                 recalculatePosition();
40209               }
40210
40211               scope.moveInProgress = false;
40212             }, eventDebounceTime);
40213
40214             // Default progress type
40215             scope.moveInProgress = false;
40216
40217             function fireRecalculating() {
40218               if (!scope.moveInProgress) {
40219                 scope.moveInProgress = true;
40220                 scope.$digest();
40221               }
40222
40223               debouncedRecalculate();
40224             }
40225
40226             // recalculate actual position and set new values to scope
40227             // after digest loop is popup in right position
40228             function recalculatePosition() {
40229               scope.position = appendToBody ? $position.offset(element) : $position.position(element);
40230               scope.position.top += element.prop('offsetHeight');
40231             }
40232
40233             //we need to propagate user's query so we can higlight matches
40234             scope.query = undefined;
40235
40236             //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
40237             var timeoutPromise;
40238
40239             var scheduleSearchWithTimeout = function(inputValue) {
40240               timeoutPromise = $timeout(function() {
40241                 getMatchesAsync(inputValue);
40242               }, waitTime);
40243             };
40244
40245             var cancelPreviousTimeout = function() {
40246               if (timeoutPromise) {
40247                 $timeout.cancel(timeoutPromise);
40248               }
40249             };
40250
40251             resetMatches();
40252
40253             scope.assignIsOpen = function (isOpen) {
40254               isOpenSetter(originalScope, isOpen);
40255             };
40256
40257             scope.select = function(activeIdx, evt) {
40258               //called from within the $digest() cycle
40259               var locals = {};
40260               var model, item;
40261
40262               selected = true;
40263               locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
40264               model = parserResult.modelMapper(originalScope, locals);
40265               $setModelValue(originalScope, model);
40266               modelCtrl.$setValidity('editable', true);
40267               modelCtrl.$setValidity('parse', true);
40268
40269               onSelectCallback(originalScope, {
40270                 $item: item,
40271                 $model: model,
40272                 $label: parserResult.viewMapper(originalScope, locals),
40273                 $event: evt
40274               });
40275
40276               resetMatches();
40277
40278               //return focus to the input element if a match was selected via a mouse click event
40279               // use timeout to avoid $rootScope:inprog error
40280               if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
40281                 $timeout(function() { element[0].focus(); }, 0, false);
40282               }
40283             };
40284
40285             //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
40286             element.on('keydown', function(evt) {
40287               //typeahead is open and an "interesting" key was pressed
40288               if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
40289                 return;
40290               }
40291
40292               // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
40293               if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
40294                 resetMatches();
40295                 scope.$digest();
40296                 return;
40297               }
40298
40299               evt.preventDefault();
40300
40301               switch (evt.which) {
40302                 case 9:
40303                 case 13:
40304                   scope.$apply(function () {
40305                     if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40306                       $$debounce(function() {
40307                         scope.select(scope.activeIdx, evt);
40308                       }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40309                     } else {
40310                       scope.select(scope.activeIdx, evt);
40311                     }
40312                   });
40313                   break;
40314                 case 27:
40315                   evt.stopPropagation();
40316
40317                   resetMatches();
40318                   scope.$digest();
40319                   break;
40320                 case 38:
40321                   scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
40322                   scope.$digest();
40323                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40324                   break;
40325                 case 40:
40326                   scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
40327                   scope.$digest();
40328                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40329                   break;
40330               }
40331             });
40332
40333             element.bind('focus', function (evt) {
40334               hasFocus = true;
40335               if (minLength === 0 && !modelCtrl.$viewValue) {
40336                 $timeout(function() {
40337                   getMatchesAsync(modelCtrl.$viewValue, evt);
40338                 }, 0);
40339               }
40340             });
40341
40342             element.bind('blur', function(evt) {
40343               if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
40344                 selected = true;
40345                 scope.$apply(function() {
40346                   if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
40347                     $$debounce(function() {
40348                       scope.select(scope.activeIdx, evt);
40349                     }, scope.debounceUpdate.blur);
40350                   } else {
40351                     scope.select(scope.activeIdx, evt);
40352                   }
40353                 });
40354               }
40355               if (!isEditable && modelCtrl.$error.editable) {
40356                 modelCtrl.$viewValue = '';
40357                 element.val('');
40358               }
40359               hasFocus = false;
40360               selected = false;
40361             });
40362
40363             // Keep reference to click handler to unbind it.
40364             var dismissClickHandler = function(evt) {
40365               // Issue #3973
40366               // Firefox treats right click as a click on document
40367               if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
40368                 resetMatches();
40369                 if (!$rootScope.$$phase) {
40370                   scope.$digest();
40371                 }
40372               }
40373             };
40374
40375             $document.on('click', dismissClickHandler);
40376
40377             originalScope.$on('$destroy', function() {
40378               $document.off('click', dismissClickHandler);
40379               if (appendToBody || appendTo) {
40380                 $popup.remove();
40381               }
40382
40383               if (appendToBody) {
40384                 angular.element($window).off('resize', fireRecalculating);
40385                 $document.find('body').off('scroll', fireRecalculating);
40386               }
40387               // Prevent jQuery cache memory leak
40388               popUpEl.remove();
40389
40390               if (showHint) {
40391                   inputsContainer.remove();
40392               }
40393             });
40394
40395             var $popup = $compile(popUpEl)(scope);
40396
40397             if (appendToBody) {
40398               $document.find('body').append($popup);
40399             } else if (appendTo) {
40400               angular.element(appendTo).eq(0).append($popup);
40401             } else {
40402               element.after($popup);
40403             }
40404
40405             this.init = function(_modelCtrl, _ngModelOptions) {
40406               modelCtrl = _modelCtrl;
40407               ngModelOptions = _ngModelOptions;
40408
40409               scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
40410
40411               //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
40412               //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
40413               modelCtrl.$parsers.unshift(function(inputValue) {
40414                 hasFocus = true;
40415
40416                 if (minLength === 0 || inputValue && inputValue.length >= minLength) {
40417                   if (waitTime > 0) {
40418                     cancelPreviousTimeout();
40419                     scheduleSearchWithTimeout(inputValue);
40420                   } else {
40421                     getMatchesAsync(inputValue);
40422                   }
40423                 } else {
40424                   isLoadingSetter(originalScope, false);
40425                   cancelPreviousTimeout();
40426                   resetMatches();
40427                 }
40428
40429                 if (isEditable) {
40430                   return inputValue;
40431                 }
40432
40433                 if (!inputValue) {
40434                   // Reset in case user had typed something previously.
40435                   modelCtrl.$setValidity('editable', true);
40436                   return null;
40437                 }
40438
40439                 modelCtrl.$setValidity('editable', false);
40440                 return undefined;
40441               });
40442
40443               modelCtrl.$formatters.push(function(modelValue) {
40444                 var candidateViewValue, emptyViewValue;
40445                 var locals = {};
40446
40447                 // The validity may be set to false via $parsers (see above) if
40448                 // the model is restricted to selected values. If the model
40449                 // is set manually it is considered to be valid.
40450                 if (!isEditable) {
40451                   modelCtrl.$setValidity('editable', true);
40452                 }
40453
40454                 if (inputFormatter) {
40455                   locals.$model = modelValue;
40456                   return inputFormatter(originalScope, locals);
40457                 }
40458
40459                 //it might happen that we don't have enough info to properly render input value
40460                 //we need to check for this situation and simply return model value if we can't apply custom formatting
40461                 locals[parserResult.itemName] = modelValue;
40462                 candidateViewValue = parserResult.viewMapper(originalScope, locals);
40463                 locals[parserResult.itemName] = undefined;
40464                 emptyViewValue = parserResult.viewMapper(originalScope, locals);
40465
40466                 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
40467               });
40468             };
40469           }])
40470
40471           .directive('uibTypeahead', function() {
40472             return {
40473               controller: 'UibTypeaheadController',
40474               require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
40475               link: function(originalScope, element, attrs, ctrls) {
40476                 ctrls[2].init(ctrls[0], ctrls[1]);
40477               }
40478             };
40479           })
40480
40481           .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
40482             return {
40483               scope: {
40484                 matches: '=',
40485                 query: '=',
40486                 active: '=',
40487                 position: '&',
40488                 moveInProgress: '=',
40489                 select: '&',
40490                 assignIsOpen: '&',
40491                 debounce: '&'
40492               },
40493               replace: true,
40494               templateUrl: function(element, attrs) {
40495                 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
40496               },
40497               link: function(scope, element, attrs) {
40498                 scope.templateUrl = attrs.templateUrl;
40499
40500                 scope.isOpen = function() {
40501                   var isDropdownOpen = scope.matches.length > 0;
40502                   scope.assignIsOpen({ isOpen: isDropdownOpen });
40503                   return isDropdownOpen;
40504                 };
40505
40506                 scope.isActive = function(matchIdx) {
40507                   return scope.active === matchIdx;
40508                 };
40509
40510                 scope.selectActive = function(matchIdx) {
40511                   scope.active = matchIdx;
40512                 };
40513
40514                 scope.selectMatch = function(activeIdx, evt) {
40515                   var debounce = scope.debounce();
40516                   if (angular.isNumber(debounce) || angular.isObject(debounce)) {
40517                     $$debounce(function() {
40518                       scope.select({activeIdx: activeIdx, evt: evt});
40519                     }, angular.isNumber(debounce) ? debounce : debounce['default']);
40520                   } else {
40521                     scope.select({activeIdx: activeIdx, evt: evt});
40522                   }
40523                 };
40524               }
40525             };
40526           }])
40527
40528           .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
40529             return {
40530               scope: {
40531                 index: '=',
40532                 match: '=',
40533                 query: '='
40534               },
40535               link: function(scope, element, attrs) {
40536                 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
40537                 $templateRequest(tplUrl).then(function(tplContent) {
40538                   var tplEl = angular.element(tplContent.trim());
40539                   element.replaceWith(tplEl);
40540                   $compile(tplEl)(scope);
40541                 });
40542               }
40543             };
40544           }])
40545
40546           .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
40547             var isSanitizePresent;
40548             isSanitizePresent = $injector.has('$sanitize');
40549
40550             function escapeRegexp(queryToEscape) {
40551               // Regex: capture the whole query string and replace it with the string that will be used to match
40552               // the results, for example if the capture is "a" the result will be \a
40553               return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
40554             }
40555
40556             function containsHtml(matchItem) {
40557               return /<.*>/g.test(matchItem);
40558             }
40559
40560             return function(matchItem, query) {
40561               if (!isSanitizePresent && containsHtml(matchItem)) {
40562                 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
40563               }
40564               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
40565               if (!isSanitizePresent) {
40566                 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
40567               }
40568               return matchItem;
40569             };
40570           }]);
40571
40572         angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
40573           $templateCache.put("uib/template/accordion/accordion-group.html",
40574             "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
40575             "  <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
40576             "    <h4 class=\"panel-title\">\n" +
40577             "      <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" +
40578             "    </h4>\n" +
40579             "  </div>\n" +
40580             "  <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
40581             "     <div class=\"panel-body\" ng-transclude></div>\n" +
40582             "  </div>\n" +
40583             "</div>\n" +
40584             "");
40585         }]);
40586
40587         angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
40588           $templateCache.put("uib/template/accordion/accordion.html",
40589             "<div class=\"panel-group\" ng-transclude></div>");
40590         }]);
40591
40592         angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
40593           $templateCache.put("uib/template/alert/alert.html",
40594             "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
40595             "    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
40596             "        <span aria-hidden=\"true\">&times;</span>\n" +
40597             "        <span class=\"sr-only\">Close</span>\n" +
40598             "    </button>\n" +
40599             "    <div ng-transclude></div>\n" +
40600             "</div>\n" +
40601             "");
40602         }]);
40603
40604         angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
40605           $templateCache.put("uib/template/carousel/carousel.html",
40606             "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
40607             "  <div class=\"carousel-inner\" ng-transclude></div>\n" +
40608             "  <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" +
40609             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
40610             "    <span class=\"sr-only\">previous</span>\n" +
40611             "  </a>\n" +
40612             "  <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" +
40613             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
40614             "    <span class=\"sr-only\">next</span>\n" +
40615             "  </a>\n" +
40616             "  <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
40617             "    <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
40618             "      <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
40619             "    </li>\n" +
40620             "  </ol>\n" +
40621             "</div>");
40622         }]);
40623
40624         angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
40625           $templateCache.put("uib/template/carousel/slide.html",
40626             "<div ng-class=\"{\n" +
40627             "    'active': active\n" +
40628             "  }\" class=\"item text-center\" ng-transclude></div>\n" +
40629             "");
40630         }]);
40631
40632         angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
40633           $templateCache.put("uib/template/datepicker/datepicker.html",
40634             "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
40635             "  <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
40636             "  <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
40637             "  <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
40638             "</div>");
40639         }]);
40640
40641         angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
40642           $templateCache.put("uib/template/datepicker/day.html",
40643             "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40644             "  <thead>\n" +
40645             "    <tr>\n" +
40646             "      <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" +
40647             "      <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" +
40648             "      <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" +
40649             "    </tr>\n" +
40650             "    <tr>\n" +
40651             "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
40652             "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
40653             "    </tr>\n" +
40654             "  </thead>\n" +
40655             "  <tbody>\n" +
40656             "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
40657             "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
40658             "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
40659             "        id=\"{{::dt.uid}}\"\n" +
40660             "        ng-class=\"::dt.customClass\">\n" +
40661             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" +
40662             "          uib-is-class=\"\n" +
40663             "            'btn-info' for selectedDt,\n" +
40664             "            'active' for activeDt\n" +
40665             "            on dt\"\n" +
40666             "          ng-click=\"select(dt.date)\"\n" +
40667             "          ng-disabled=\"::dt.disabled\"\n" +
40668             "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40669             "      </td>\n" +
40670             "    </tr>\n" +
40671             "  </tbody>\n" +
40672             "</table>\n" +
40673             "");
40674         }]);
40675
40676         angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
40677           $templateCache.put("uib/template/datepicker/month.html",
40678             "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40679             "  <thead>\n" +
40680             "    <tr>\n" +
40681             "      <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" +
40682             "      <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" +
40683             "      <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" +
40684             "    </tr>\n" +
40685             "  </thead>\n" +
40686             "  <tbody>\n" +
40687             "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
40688             "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
40689             "        id=\"{{::dt.uid}}\"\n" +
40690             "        ng-class=\"::dt.customClass\">\n" +
40691             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40692             "          uib-is-class=\"\n" +
40693             "            'btn-info' for selectedDt,\n" +
40694             "            'active' for activeDt\n" +
40695             "            on dt\"\n" +
40696             "          ng-click=\"select(dt.date)\"\n" +
40697             "          ng-disabled=\"::dt.disabled\"\n" +
40698             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40699             "      </td>\n" +
40700             "    </tr>\n" +
40701             "  </tbody>\n" +
40702             "</table>\n" +
40703             "");
40704         }]);
40705
40706         angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
40707           $templateCache.put("uib/template/datepicker/popup.html",
40708             "<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" +
40709             "   <li ng-transclude></li>\n" +
40710             "   <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" +
40711             "           <span class=\"btn-group pull-left\">\n" +
40712             "                   <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
40713             "                   <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
40714             "           </span>\n" +
40715             "           <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
40716             "   </li>\n" +
40717             "</ul>\n" +
40718             "");
40719         }]);
40720
40721         angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
40722           $templateCache.put("uib/template/datepicker/year.html",
40723             "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40724             "  <thead>\n" +
40725             "    <tr>\n" +
40726             "      <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" +
40727             "      <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" +
40728             "      <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" +
40729             "    </tr>\n" +
40730             "  </thead>\n" +
40731             "  <tbody>\n" +
40732             "    <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
40733             "      <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
40734             "        id=\"{{::dt.uid}}\"\n" +
40735             "        ng-class=\"::dt.customClass\">\n" +
40736             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40737             "          uib-is-class=\"\n" +
40738             "            'btn-info' for selectedDt,\n" +
40739             "            'active' for activeDt\n" +
40740             "            on dt\"\n" +
40741             "          ng-click=\"select(dt.date)\"\n" +
40742             "          ng-disabled=\"::dt.disabled\"\n" +
40743             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40744             "      </td>\n" +
40745             "    </tr>\n" +
40746             "  </tbody>\n" +
40747             "</table>\n" +
40748             "");
40749         }]);
40750
40751         angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
40752           $templateCache.put("uib/template/modal/backdrop.html",
40753             "<div class=\"modal-backdrop\"\n" +
40754             "     uib-modal-animation-class=\"fade\"\n" +
40755             "     modal-in-class=\"in\"\n" +
40756             "     ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
40757             "></div>\n" +
40758             "");
40759         }]);
40760
40761         angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
40762           $templateCache.put("uib/template/modal/window.html",
40763             "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
40764             "    uib-modal-animation-class=\"fade\"\n" +
40765             "    modal-in-class=\"in\"\n" +
40766             "    ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
40767             "    <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
40768             "</div>\n" +
40769             "");
40770         }]);
40771
40772         angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
40773           $templateCache.put("uib/template/pager/pager.html",
40774             "<ul class=\"pager\">\n" +
40775             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40776             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40777             "</ul>\n" +
40778             "");
40779         }]);
40780
40781         angular.module("uib/template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
40782           $templateCache.put("uib/template/pagination/pager.html",
40783             "<ul class=\"pager\">\n" +
40784             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40785             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40786             "</ul>\n" +
40787             "");
40788         }]);
40789
40790         angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
40791           $templateCache.put("uib/template/pagination/pagination.html",
40792             "<ul class=\"pagination\">\n" +
40793             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
40794             "  <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" +
40795             "  <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" +
40796             "  <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" +
40797             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
40798             "</ul>\n" +
40799             "");
40800         }]);
40801
40802         angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
40803           $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
40804             "<div class=\"tooltip\"\n" +
40805             "  tooltip-animation-class=\"fade\"\n" +
40806             "  uib-tooltip-classes\n" +
40807             "  ng-class=\"{ in: isOpen() }\">\n" +
40808             "  <div class=\"tooltip-arrow\"></div>\n" +
40809             "  <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
40810             "</div>\n" +
40811             "");
40812         }]);
40813
40814         angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
40815           $templateCache.put("uib/template/tooltip/tooltip-popup.html",
40816             "<div class=\"tooltip\"\n" +
40817             "  tooltip-animation-class=\"fade\"\n" +
40818             "  uib-tooltip-classes\n" +
40819             "  ng-class=\"{ in: isOpen() }\">\n" +
40820             "  <div class=\"tooltip-arrow\"></div>\n" +
40821             "  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
40822             "</div>\n" +
40823             "");
40824         }]);
40825
40826         angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
40827           $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
40828             "<div class=\"tooltip\"\n" +
40829             "  tooltip-animation-class=\"fade\"\n" +
40830             "  uib-tooltip-classes\n" +
40831             "  ng-class=\"{ in: isOpen() }\">\n" +
40832             "  <div class=\"tooltip-arrow\"></div>\n" +
40833             "  <div class=\"tooltip-inner\"\n" +
40834             "    uib-tooltip-template-transclude=\"contentExp()\"\n" +
40835             "    tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40836             "</div>\n" +
40837             "");
40838         }]);
40839
40840         angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
40841           $templateCache.put("uib/template/popover/popover-html.html",
40842             "<div class=\"popover\"\n" +
40843             "  tooltip-animation-class=\"fade\"\n" +
40844             "  uib-tooltip-classes\n" +
40845             "  ng-class=\"{ in: isOpen() }\">\n" +
40846             "  <div class=\"arrow\"></div>\n" +
40847             "\n" +
40848             "  <div class=\"popover-inner\">\n" +
40849             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40850             "      <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
40851             "  </div>\n" +
40852             "</div>\n" +
40853             "");
40854         }]);
40855
40856         angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
40857           $templateCache.put("uib/template/popover/popover-template.html",
40858             "<div class=\"popover\"\n" +
40859             "  tooltip-animation-class=\"fade\"\n" +
40860             "  uib-tooltip-classes\n" +
40861             "  ng-class=\"{ in: isOpen() }\">\n" +
40862             "  <div class=\"arrow\"></div>\n" +
40863             "\n" +
40864             "  <div class=\"popover-inner\">\n" +
40865             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40866             "      <div class=\"popover-content\"\n" +
40867             "        uib-tooltip-template-transclude=\"contentExp()\"\n" +
40868             "        tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40869             "  </div>\n" +
40870             "</div>\n" +
40871             "");
40872         }]);
40873
40874         angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
40875           $templateCache.put("uib/template/popover/popover.html",
40876             "<div class=\"popover\"\n" +
40877             "  tooltip-animation-class=\"fade\"\n" +
40878             "  uib-tooltip-classes\n" +
40879             "  ng-class=\"{ in: isOpen() }\">\n" +
40880             "  <div class=\"arrow\"></div>\n" +
40881             "\n" +
40882             "  <div class=\"popover-inner\">\n" +
40883             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40884             "      <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
40885             "  </div>\n" +
40886             "</div>\n" +
40887             "");
40888         }]);
40889
40890         angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
40891           $templateCache.put("uib/template/progressbar/bar.html",
40892             "<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" +
40893             "");
40894         }]);
40895
40896         angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
40897           $templateCache.put("uib/template/progressbar/progress.html",
40898             "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
40899         }]);
40900
40901         angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
40902           $templateCache.put("uib/template/progressbar/progressbar.html",
40903             "<div class=\"progress\">\n" +
40904             "  <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" +
40905             "</div>\n" +
40906             "");
40907         }]);
40908
40909         angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
40910           $templateCache.put("uib/template/rating/rating.html",
40911             "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
40912             "    <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
40913             "    <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" +
40914             "</span>\n" +
40915             "");
40916         }]);
40917
40918         angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
40919           $templateCache.put("uib/template/tabs/tab.html",
40920             "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" +
40921             "  <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" +
40922             "</li>\n" +
40923             "");
40924         }]);
40925
40926         angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
40927           $templateCache.put("uib/template/tabs/tabset.html",
40928             "<div>\n" +
40929             "  <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
40930             "  <div class=\"tab-content\">\n" +
40931             "    <div class=\"tab-pane\" \n" +
40932             "         ng-repeat=\"tab in tabs\" \n" +
40933             "         ng-class=\"{active: tab.active}\"\n" +
40934             "         uib-tab-content-transclude=\"tab\">\n" +
40935             "    </div>\n" +
40936             "  </div>\n" +
40937             "</div>\n" +
40938             "");
40939         }]);
40940
40941         angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
40942           $templateCache.put("uib/template/timepicker/timepicker.html",
40943             "<table class=\"uib-timepicker\">\n" +
40944             "  <tbody>\n" +
40945             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40946             "      <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" +
40947             "      <td>&nbsp;</td>\n" +
40948             "      <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" +
40949             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40950             "      <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" +
40951             "      <td ng-show=\"showMeridian\"></td>\n" +
40952             "    </tr>\n" +
40953             "    <tr>\n" +
40954             "      <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
40955             "        <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" +
40956             "      </td>\n" +
40957             "      <td class=\"uib-separator\">:</td>\n" +
40958             "      <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
40959             "        <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" +
40960             "      </td>\n" +
40961             "      <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
40962             "      <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
40963             "        <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" +
40964             "      </td>\n" +
40965             "      <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" +
40966             "    </tr>\n" +
40967             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40968             "      <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" +
40969             "      <td>&nbsp;</td>\n" +
40970             "      <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" +
40971             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40972             "      <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" +
40973             "      <td ng-show=\"showMeridian\"></td>\n" +
40974             "    </tr>\n" +
40975             "  </tbody>\n" +
40976             "</table>\n" +
40977             "");
40978         }]);
40979
40980         angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
40981           $templateCache.put("uib/template/typeahead/typeahead-match.html",
40982             "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" +
40983             "");
40984         }]);
40985
40986         angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
40987           $templateCache.put("uib/template/typeahead/typeahead-popup.html",
40988             "<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" +
40989             "    <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" +
40990             "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
40991             "    </li>\n" +
40992             "</ul>\n" +
40993             "");
40994         }]);
40995         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>'); })
40996
40997 /***/ },
40998 /* 8 */
40999 /***/ function(module, exports) {
41000
41001         var app;
41002         (function (app) {
41003             var declares;
41004             (function (declares) {
41005                 var CommandInfo = (function () {
41006                     function CommandInfo(name) {
41007                         this.name = name;
41008                     }
41009                     return CommandInfo;
41010                 })();
41011                 declares.CommandInfo = CommandInfo;
41012             })(declares = app.declares || (app.declares = {}));
41013         })(app || (app = {}));
41014         var app;
41015         (function (app) {
41016             var services;
41017             (function (services) {
41018                 var APIEndPoint = (function () {
41019                     function APIEndPoint($resource) {
41020                         this.$resource = $resource;
41021                     }
41022                     APIEndPoint.prototype.resource = function (endPoint) {
41023                         var customAction = {
41024                             method: 'GET',
41025                             isArray: false
41026                         };
41027                         return this.$resource(endPoint, {}, { customAction: customAction });
41028                     };
41029                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41030                         var endPoint = '/api/optionControlFile/' + command;
41031                         return this.resource(endPoint).get();
41032                     };
41033                     APIEndPoint.prototype.getFiles = function (fileId) {
41034                         var endPoint = '/api/v1/workspace';
41035                         if (fileId) {
41036                             endPoint += '/' + fileId;
41037                         }
41038                         return this.resource(endPoint).get();
41039                     };
41040                     APIEndPoint.prototype.getDirectories = function () {
41041                         var endPoint = '/api/v1/all/workspace/directory';
41042                         return this.resource(endPoint).get();
41043                     };
41044                     return APIEndPoint;
41045                 })();
41046                 services.APIEndPoint = APIEndPoint;
41047             })(services = app.services || (app.services = {}));
41048         })(app || (app = {}));
41049         var app;
41050         (function (app) {
41051             var services;
41052             (function (services) {
41053                 var MyModal = (function () {
41054                     function MyModal($uibModal) {
41055                         this.$uibModal = $uibModal;
41056                         this.modalOption = {
41057                             backdrop: true,
41058                             controller: null,
41059                             templateUrl: null,
41060                             size: null
41061                         };
41062                     }
41063                     MyModal.prototype.open = function (modalName) {
41064                         if (modalName === 'SelectCommand') {
41065                             this.modalOption.templateUrl = 'templates/select-command.html';
41066                             this.modalOption.size = 'lg';
41067                         }
41068                         return this.$uibModal.open(this.modalOption);
41069                     };
41070                     return MyModal;
41071                 })();
41072                 services.MyModal = MyModal;
41073             })(services = app.services || (app.services = {}));
41074         })(app || (app = {}));
41075         var app;
41076         (function (app) {
41077             var directives;
41078             (function (directives) {
41079                 var Command = (function () {
41080                     function Command() {
41081                         this.restrict = 'E';
41082                         this.replace = true;
41083                         this.scope = true;
41084                         this.controller = 'commandController';
41085                         this.controllerAs = 'ctrl';
41086                         this.bindToController = {
41087                             index: '=',
41088                             name: '=',
41089                             remove: '&',
41090                             list: '='
41091                         };
41092                         this.templateUrl = 'templates/command.html';
41093                     }
41094                     Command.Factory = function () {
41095                         var directive = function () {
41096                             return new Command();
41097                         };
41098                         directive.$inject = [];
41099                         return directive;
41100                     };
41101                     return Command;
41102                 })();
41103                 directives.Command = Command;
41104                 var CommandController = (function () {
41105                     function CommandController(APIEndPoint, $scope) {
41106                         this.APIEndPoint = APIEndPoint;
41107                         this.$scope = $scope;
41108                         var controller = this;
41109                         this.APIEndPoint
41110                             .getOptionControlFile('dcdFilePrint')
41111                             .$promise
41112                             .then(function (result) {
41113                             controller.options = result.info;
41114                         });
41115                         this.APIEndPoint
41116                             .getDirectories()
41117                             .$promise
41118                             .then(function (result) {
41119                             controller.dirs = result.info;
41120                         });
41121                         this.heading = "[" + this.index + "]: dcdFilePring";
41122                         this.isOpen = true;
41123                         this.$scope.$on('close', function () {
41124                             controller.isOpen = false;
41125                         });
41126                     }
41127                     CommandController.prototype.submit = function () {
41128                         var opt = [];
41129                         angular.forEach(this.options, function (option) {
41130                             var obj = {
41131                                 name: option.option,
41132                                 arguments: []
41133                             };
41134                             angular.forEach(option.arg, function (arg) {
41135                                 if (arg.input) {
41136                                     obj.arguments.push(arg.input);
41137                                 }
41138                             });
41139                             if (obj.arguments.length > 0) {
41140                                 opt.push(obj);
41141                             }
41142                         });
41143                         var execObj = {
41144                             command: this.name,
41145                             workspace: this.workspace.fileId,
41146                             option: opt
41147                         };
41148                         console.log(JSON.stringify(execObj, null, '\t'));
41149                     };
41150                     CommandController.prototype.removeMySelf = function (index) {
41151                         this.remove()(index, this.list);
41152                     };
41153                     CommandController.prototype.reloadFiles = function () {
41154                         var _this = this;
41155                         var fileId = this.workspace.fileId;
41156                         this.APIEndPoint
41157                             .getFiles(fileId)
41158                             .$promise
41159                             .then(function (result) {
41160                             var status = result.status;
41161                             if (status === 'success') {
41162                                 _this.files = result.info;
41163                             }
41164                             else {
41165                                 console.log(result.message);
41166                             }
41167                         });
41168                     };
41169                     CommandController.prototype.debug = function () {
41170                         console.log(this.files);
41171                         console.log(this.files);
41172                         console.log(this.workspace);
41173                     };
41174                     CommandController.$inject = ['APIEndPoint', '$scope'];
41175                     return CommandController;
41176                 })();
41177                 directives.CommandController = CommandController;
41178             })(directives = app.directives || (app.directives = {}));
41179         })(app || (app = {}));
41180         var app;
41181         (function (app) {
41182             var directives;
41183             (function (directives) {
41184                 var HeaderMenu = (function () {
41185                     function HeaderMenu() {
41186                         this.restrict = 'E';
41187                         this.replace = true;
41188                         this.templateUrl = 'templates/header-menu.html';
41189                     }
41190                     HeaderMenu.Factory = function () {
41191                         var directive = function () {
41192                             return new HeaderMenu();
41193                         };
41194                         return directive;
41195                     };
41196                     return HeaderMenu;
41197                 })();
41198                 directives.HeaderMenu = HeaderMenu;
41199             })(directives = app.directives || (app.directives = {}));
41200         })(app || (app = {}));
41201         var app;
41202         (function (app) {
41203             var directives;
41204             (function (directives) {
41205                 var Option = (function () {
41206                     function Option() {
41207                         this.restrict = 'E';
41208                         this.replace = true;
41209                         this.controller = 'optionController';
41210                         this.bindToController = {
41211                             info: '=',
41212                             files: '='
41213                         };
41214                         this.scope = true;
41215                         this.templateUrl = 'templates/option.html';
41216                         this.controllerAs = 'ctrl';
41217                     }
41218                     Option.Factory = function () {
41219                         var directive = function () {
41220                             return new Option();
41221                         };
41222                         directive.$inject = [];
41223                         return directive;
41224                     };
41225                     return Option;
41226                 })();
41227                 directives.Option = Option;
41228                 var OptionController = (function () {
41229                     function OptionController() {
41230                         var controller = this;
41231                         angular.forEach(controller.info.arg, function (arg) {
41232                             if (arg.initialValue) {
41233                                 if (arg.formType === 'number') {
41234                                     arg.input = parseInt(arg.initialValue);
41235                                 }
41236                                 else {
41237                                     arg.input = arg.initialValue;
41238                                 }
41239                             }
41240                         });
41241                     }
41242                     OptionController.$inject = [];
41243                     return OptionController;
41244                 })();
41245                 directives.OptionController = OptionController;
41246             })(directives = app.directives || (app.directives = {}));
41247         })(app || (app = {}));
41248         var app;
41249         (function (app) {
41250             var directives;
41251             (function (directives) {
41252                 var Directory = (function () {
41253                     function Directory() {
41254                         this.restrict = 'E';
41255                         this.replace = true;
41256                         this.controller = 'directoryController';
41257                         this.controllerAs = 'ctrl';
41258                         this.bindToController = {
41259                             info: '=',
41260                             add: '&',
41261                             list: '=',
41262                             files: '='
41263                         };
41264                         this.templateUrl = 'templates/directory.html';
41265                     }
41266                     Directory.Factory = function () {
41267                         var directive = function () {
41268                             return new Directory();
41269                         };
41270                         return directive;
41271                     };
41272                     return Directory;
41273                 })();
41274                 directives.Directory = Directory;
41275                 var DirectoryController = (function () {
41276                     function DirectoryController(APIEndPoint, $scope) {
41277                         this.APIEndPoint = APIEndPoint;
41278                         this.$scope = $scope;
41279                         var controller = this;
41280                         this.APIEndPoint
41281                             .getFiles(this.info.fileId)
41282                             .$promise
41283                             .then(function (result) {
41284                             if (result.status === 'success') {
41285                                 controller.files = result.info;
41286                                 angular.forEach(result.info, function (file) {
41287                                     if (file.fileType === '0') {
41288                                         var o = file;
41289                                         if (controller.info.path === '/') {
41290                                             o.path = '/' + file.name;
41291                                         }
41292                                         else {
41293                                             o.path = controller.info.path + '/' + file.name;
41294                                         }
41295                                         controller.add()(o, controller.list);
41296                                     }
41297                                 });
41298                             }
41299                             ;
41300                         });
41301                     }
41302                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41303                     return DirectoryController;
41304                 })();
41305                 directives.DirectoryController = DirectoryController;
41306             })(directives = app.directives || (app.directives = {}));
41307         })(app || (app = {}));
41308         var app;
41309         (function (app) {
41310             var controllers;
41311             (function (controllers) {
41312                 var Execution = (function () {
41313                     function Execution(MyModal, $scope) {
41314                         this.MyModal = MyModal;
41315                         this.$scope = $scope;
41316                         this.commandInfoList = [];
41317                     }
41318                     ;
41319                     Execution.prototype.add = function () {
41320                         this.$scope.$broadcast('close');
41321                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
41322                     };
41323                     Execution.prototype.open = function () {
41324                         var result = this.MyModal.open('SelectCommand');
41325                         console.log(result);
41326                     };
41327                     Execution.prototype.remove = function (index, list) {
41328                         list.splice(index, 1);
41329                     };
41330                     Execution.prototype.close = function () {
41331                         console.log("close");
41332                     };
41333                     Execution.$inject = ['MyModal', '$scope'];
41334                     return Execution;
41335                 })();
41336                 controllers.Execution = Execution;
41337             })(controllers = app.controllers || (app.controllers = {}));
41338         })(app || (app = {}));
41339         var app;
41340         (function (app) {
41341             var controllers;
41342             (function (controllers) {
41343                 var Workspace = (function () {
41344                     function Workspace($scope, APIEndPoint) {
41345                         this.$scope = $scope;
41346                         this.APIEndPoint = APIEndPoint;
41347                         this.directoryList = [];
41348                         var controller = this;
41349                         var directoryList = this.directoryList;
41350                         var o = {
41351                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41352                             name: '',
41353                             parentId: '',
41354                             fileType: '',
41355                             createdAt: '',
41356                             updatedAt: '',
41357                             path: '/'
41358                         };
41359                         directoryList.push(o);
41360                     }
41361                     Workspace.prototype.addDirectory = function (info, directoryList) {
41362                         directoryList.push(info);
41363                     };
41364                     Workspace.$inject = ['$scope', 'APIEndPoint'];
41365                     return Workspace;
41366                 })();
41367                 controllers.Workspace = Workspace;
41368             })(controllers = app.controllers || (app.controllers = {}));
41369         })(app || (app = {}));
41370         var app;
41371         (function (app) {
41372             var controllers;
41373             (function (controllers) {
41374                 var History = (function () {
41375                     function History($scope) {
41376                         this.page = "History";
41377                     }
41378                     History.$inject = ['$scope'];
41379                     return History;
41380                 })();
41381                 controllers.History = History;
41382             })(controllers = app.controllers || (app.controllers = {}));
41383         })(app || (app = {}));
41384         var app;
41385         (function (app) {
41386             'use strict';
41387             var appName = 'zephyr';
41388             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41389             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41390                 $urlRouterProvider.otherwise('/execution');
41391                 $locationProvider.html5Mode({
41392                     enabled: true,
41393                     requireBase: false
41394                 });
41395                 $stateProvider
41396                     .state('execution', {
41397                     url: '/execution',
41398                     templateUrl: 'templates/execution.html',
41399                     controller: 'executionController',
41400                     controllerAs: 'c'
41401                 })
41402                     .state('workspace', {
41403                     url: '/workspace',
41404                     templateUrl: 'templates/workspace.html',
41405                     controller: 'workspaceController',
41406                     controllerAs: 'c'
41407                 })
41408                     .state('history', {
41409                     url: '/history',
41410                     templateUrl: 'templates/history.html',
41411                     controller: 'historyController',
41412                     controllerAs: 'c'
41413                 });
41414             });
41415             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41416             app.zephyr.service('MyModal', app.services.MyModal);
41417             app.zephyr.controller('executionController', app.controllers.Execution);
41418             app.zephyr.controller('workspaceController', app.controllers.Workspace);
41419             app.zephyr.controller('historyController', app.controllers.History);
41420             app.zephyr.controller('commandController', app.directives.CommandController);
41421             app.zephyr.controller('optionController', app.directives.OptionController);
41422             app.zephyr.controller('directoryController', app.directives.DirectoryController);
41423             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41424             app.zephyr.directive('command', app.directives.Command.Factory());
41425             app.zephyr.directive('option', app.directives.Option.Factory());
41426             app.zephyr.directive('directory', app.directives.Directory.Factory());
41427         })(app || (app = {}));
41428
41429
41430 /***/ },
41431 /* 9 */
41432 /***/ function(module, exports) {
41433
41434         var app;
41435         (function (app) {
41436             var declares;
41437             (function (declares) {
41438                 var CommandInfo = (function () {
41439                     function CommandInfo(name) {
41440                         this.name = name;
41441                     }
41442                     return CommandInfo;
41443                 })();
41444                 declares.CommandInfo = CommandInfo;
41445             })(declares = app.declares || (app.declares = {}));
41446         })(app || (app = {}));
41447         var app;
41448         (function (app) {
41449             var services;
41450             (function (services) {
41451                 var APIEndPoint = (function () {
41452                     function APIEndPoint($resource) {
41453                         this.$resource = $resource;
41454                     }
41455                     APIEndPoint.prototype.resource = function (endPoint) {
41456                         var customAction = {
41457                             method: 'GET',
41458                             isArray: false
41459                         };
41460                         return this.$resource(endPoint, {}, { customAction: customAction });
41461                     };
41462                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41463                         var endPoint = '/api/optionControlFile/' + command;
41464                         return this.resource(endPoint).get();
41465                     };
41466                     APIEndPoint.prototype.getFiles = function (fileId) {
41467                         var endPoint = '/api/v1/workspace';
41468                         if (fileId) {
41469                             endPoint += '/' + fileId;
41470                         }
41471                         return this.resource(endPoint).get();
41472                     };
41473                     APIEndPoint.prototype.getDirectories = function () {
41474                         var endPoint = '/api/v1/all/workspace/directory';
41475                         return this.resource(endPoint).get();
41476                     };
41477                     return APIEndPoint;
41478                 })();
41479                 services.APIEndPoint = APIEndPoint;
41480             })(services = app.services || (app.services = {}));
41481         })(app || (app = {}));
41482         var app;
41483         (function (app) {
41484             var services;
41485             (function (services) {
41486                 var MyModal = (function () {
41487                     function MyModal($uibModal) {
41488                         this.$uibModal = $uibModal;
41489                         this.modalOption = {
41490                             backdrop: true,
41491                             controller: null,
41492                             templateUrl: null,
41493                             size: null
41494                         };
41495                     }
41496                     MyModal.prototype.open = function (modalName) {
41497                         if (modalName === 'SelectCommand') {
41498                             this.modalOption.templateUrl = 'templates/select-command.html';
41499                             this.modalOption.size = 'lg';
41500                         }
41501                         return this.$uibModal.open(this.modalOption);
41502                     };
41503                     return MyModal;
41504                 })();
41505                 services.MyModal = MyModal;
41506             })(services = app.services || (app.services = {}));
41507         })(app || (app = {}));
41508         var app;
41509         (function (app) {
41510             var directives;
41511             (function (directives) {
41512                 var Command = (function () {
41513                     function Command() {
41514                         this.restrict = 'E';
41515                         this.replace = true;
41516                         this.scope = true;
41517                         this.controller = 'commandController';
41518                         this.controllerAs = 'ctrl';
41519                         this.bindToController = {
41520                             index: '=',
41521                             name: '=',
41522                             remove: '&',
41523                             list: '='
41524                         };
41525                         this.templateUrl = 'templates/command.html';
41526                     }
41527                     Command.Factory = function () {
41528                         var directive = function () {
41529                             return new Command();
41530                         };
41531                         directive.$inject = [];
41532                         return directive;
41533                     };
41534                     return Command;
41535                 })();
41536                 directives.Command = Command;
41537                 var CommandController = (function () {
41538                     function CommandController(APIEndPoint, $scope) {
41539                         this.APIEndPoint = APIEndPoint;
41540                         this.$scope = $scope;
41541                         var controller = this;
41542                         this.APIEndPoint
41543                             .getOptionControlFile('dcdFilePrint')
41544                             .$promise
41545                             .then(function (result) {
41546                             controller.options = result.info;
41547                         });
41548                         this.APIEndPoint
41549                             .getDirectories()
41550                             .$promise
41551                             .then(function (result) {
41552                             controller.dirs = result.info;
41553                         });
41554                         this.heading = "[" + this.index + "]: dcdFilePring";
41555                         this.isOpen = true;
41556                         this.$scope.$on('close', function () {
41557                             controller.isOpen = false;
41558                         });
41559                     }
41560                     CommandController.prototype.submit = function () {
41561                         var opt = [];
41562                         angular.forEach(this.options, function (option) {
41563                             var obj = {
41564                                 name: option.option,
41565                                 arguments: []
41566                             };
41567                             angular.forEach(option.arg, function (arg) {
41568                                 if (arg.input) {
41569                                     obj.arguments.push(arg.input);
41570                                 }
41571                             });
41572                             if (obj.arguments.length > 0) {
41573                                 opt.push(obj);
41574                             }
41575                         });
41576                         var execObj = {
41577                             command: this.name,
41578                             workspace: this.workspace.fileId,
41579                             option: opt
41580                         };
41581                         console.log(JSON.stringify(execObj, null, '\t'));
41582                     };
41583                     CommandController.prototype.removeMySelf = function (index) {
41584                         this.remove()(index, this.list);
41585                     };
41586                     CommandController.prototype.reloadFiles = function () {
41587                         var _this = this;
41588                         var fileId = this.workspace.fileId;
41589                         this.APIEndPoint
41590                             .getFiles(fileId)
41591                             .$promise
41592                             .then(function (result) {
41593                             var status = result.status;
41594                             if (status === 'success') {
41595                                 _this.files = result.info;
41596                             }
41597                             else {
41598                                 console.log(result.message);
41599                             }
41600                         });
41601                     };
41602                     CommandController.prototype.debug = function () {
41603                         console.log(this.files);
41604                         console.log(this.files);
41605                         console.log(this.workspace);
41606                     };
41607                     CommandController.$inject = ['APIEndPoint', '$scope'];
41608                     return CommandController;
41609                 })();
41610                 directives.CommandController = CommandController;
41611             })(directives = app.directives || (app.directives = {}));
41612         })(app || (app = {}));
41613         var app;
41614         (function (app) {
41615             var directives;
41616             (function (directives) {
41617                 var HeaderMenu = (function () {
41618                     function HeaderMenu() {
41619                         this.restrict = 'E';
41620                         this.replace = true;
41621                         this.templateUrl = 'templates/header-menu.html';
41622                     }
41623                     HeaderMenu.Factory = function () {
41624                         var directive = function () {
41625                             return new HeaderMenu();
41626                         };
41627                         return directive;
41628                     };
41629                     return HeaderMenu;
41630                 })();
41631                 directives.HeaderMenu = HeaderMenu;
41632             })(directives = app.directives || (app.directives = {}));
41633         })(app || (app = {}));
41634         var app;
41635         (function (app) {
41636             var directives;
41637             (function (directives) {
41638                 var Option = (function () {
41639                     function Option() {
41640                         this.restrict = 'E';
41641                         this.replace = true;
41642                         this.controller = 'optionController';
41643                         this.bindToController = {
41644                             info: '=',
41645                             files: '='
41646                         };
41647                         this.scope = true;
41648                         this.templateUrl = 'templates/option.html';
41649                         this.controllerAs = 'ctrl';
41650                     }
41651                     Option.Factory = function () {
41652                         var directive = function () {
41653                             return new Option();
41654                         };
41655                         directive.$inject = [];
41656                         return directive;
41657                     };
41658                     return Option;
41659                 })();
41660                 directives.Option = Option;
41661                 var OptionController = (function () {
41662                     function OptionController() {
41663                         var controller = this;
41664                         angular.forEach(controller.info.arg, function (arg) {
41665                             if (arg.initialValue) {
41666                                 if (arg.formType === 'number') {
41667                                     arg.input = parseInt(arg.initialValue);
41668                                 }
41669                                 else {
41670                                     arg.input = arg.initialValue;
41671                                 }
41672                             }
41673                         });
41674                     }
41675                     OptionController.$inject = [];
41676                     return OptionController;
41677                 })();
41678                 directives.OptionController = OptionController;
41679             })(directives = app.directives || (app.directives = {}));
41680         })(app || (app = {}));
41681         var app;
41682         (function (app) {
41683             var directives;
41684             (function (directives) {
41685                 var Directory = (function () {
41686                     function Directory() {
41687                         this.restrict = 'E';
41688                         this.replace = true;
41689                         this.controller = 'directoryController';
41690                         this.controllerAs = 'ctrl';
41691                         this.bindToController = {
41692                             info: '=',
41693                             add: '&',
41694                             list: '=',
41695                             files: '='
41696                         };
41697                         this.templateUrl = 'templates/directory.html';
41698                     }
41699                     Directory.Factory = function () {
41700                         var directive = function () {
41701                             return new Directory();
41702                         };
41703                         return directive;
41704                     };
41705                     return Directory;
41706                 })();
41707                 directives.Directory = Directory;
41708                 var DirectoryController = (function () {
41709                     function DirectoryController(APIEndPoint, $scope) {
41710                         this.APIEndPoint = APIEndPoint;
41711                         this.$scope = $scope;
41712                         var controller = this;
41713                         this.APIEndPoint
41714                             .getFiles(this.info.fileId)
41715                             .$promise
41716                             .then(function (result) {
41717                             if (result.status === 'success') {
41718                                 controller.files = result.info;
41719                                 angular.forEach(result.info, function (file) {
41720                                     if (file.fileType === '0') {
41721                                         var o = file;
41722                                         if (controller.info.path === '/') {
41723                                             o.path = '/' + file.name;
41724                                         }
41725                                         else {
41726                                             o.path = controller.info.path + '/' + file.name;
41727                                         }
41728                                         controller.add()(o, controller.list);
41729                                     }
41730                                 });
41731                             }
41732                             ;
41733                         });
41734                     }
41735                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41736                     return DirectoryController;
41737                 })();
41738                 directives.DirectoryController = DirectoryController;
41739             })(directives = app.directives || (app.directives = {}));
41740         })(app || (app = {}));
41741         var app;
41742         (function (app) {
41743             var controllers;
41744             (function (controllers) {
41745                 var Execution = (function () {
41746                     function Execution(MyModal, $scope) {
41747                         this.MyModal = MyModal;
41748                         this.$scope = $scope;
41749                         this.commandInfoList = [];
41750                     }
41751                     ;
41752                     Execution.prototype.add = function () {
41753                         this.$scope.$broadcast('close');
41754                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
41755                     };
41756                     Execution.prototype.open = function () {
41757                         var result = this.MyModal.open('SelectCommand');
41758                         console.log(result);
41759                     };
41760                     Execution.prototype.remove = function (index, list) {
41761                         list.splice(index, 1);
41762                     };
41763                     Execution.prototype.close = function () {
41764                         console.log("close");
41765                     };
41766                     Execution.$inject = ['MyModal', '$scope'];
41767                     return Execution;
41768                 })();
41769                 controllers.Execution = Execution;
41770             })(controllers = app.controllers || (app.controllers = {}));
41771         })(app || (app = {}));
41772         var app;
41773         (function (app) {
41774             var controllers;
41775             (function (controllers) {
41776                 var Workspace = (function () {
41777                     function Workspace($scope, APIEndPoint) {
41778                         this.$scope = $scope;
41779                         this.APIEndPoint = APIEndPoint;
41780                         this.directoryList = [];
41781                         var controller = this;
41782                         var directoryList = this.directoryList;
41783                         var o = {
41784                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41785                             name: '',
41786                             parentId: '',
41787                             fileType: '',
41788                             createdAt: '',
41789                             updatedAt: '',
41790                             path: '/'
41791                         };
41792                         directoryList.push(o);
41793                     }
41794                     Workspace.prototype.addDirectory = function (info, directoryList) {
41795                         directoryList.push(info);
41796                     };
41797                     Workspace.$inject = ['$scope', 'APIEndPoint'];
41798                     return Workspace;
41799                 })();
41800                 controllers.Workspace = Workspace;
41801             })(controllers = app.controllers || (app.controllers = {}));
41802         })(app || (app = {}));
41803         var app;
41804         (function (app) {
41805             var controllers;
41806             (function (controllers) {
41807                 var History = (function () {
41808                     function History($scope) {
41809                         this.page = "History";
41810                     }
41811                     History.$inject = ['$scope'];
41812                     return History;
41813                 })();
41814                 controllers.History = History;
41815             })(controllers = app.controllers || (app.controllers = {}));
41816         })(app || (app = {}));
41817         var app;
41818         (function (app) {
41819             'use strict';
41820             var appName = 'zephyr';
41821             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41822             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41823                 $urlRouterProvider.otherwise('/execution');
41824                 $locationProvider.html5Mode({
41825                     enabled: true,
41826                     requireBase: false
41827                 });
41828                 $stateProvider
41829                     .state('execution', {
41830                     url: '/execution',
41831                     templateUrl: 'templates/execution.html',
41832                     controller: 'executionController',
41833                     controllerAs: 'c'
41834                 })
41835                     .state('workspace', {
41836                     url: '/workspace',
41837                     templateUrl: 'templates/workspace.html',
41838                     controller: 'workspaceController',
41839                     controllerAs: 'c'
41840                 })
41841                     .state('history', {
41842                     url: '/history',
41843                     templateUrl: 'templates/history.html',
41844                     controller: 'historyController',
41845                     controllerAs: 'c'
41846                 });
41847             });
41848             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41849             app.zephyr.service('MyModal', app.services.MyModal);
41850             app.zephyr.controller('executionController', app.controllers.Execution);
41851             app.zephyr.controller('workspaceController', app.controllers.Workspace);
41852             app.zephyr.controller('historyController', app.controllers.History);
41853             app.zephyr.controller('commandController', app.directives.CommandController);
41854             app.zephyr.controller('optionController', app.directives.OptionController);
41855             app.zephyr.controller('directoryController', app.directives.DirectoryController);
41856             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41857             app.zephyr.directive('command', app.directives.Command.Factory());
41858             app.zephyr.directive('option', app.directives.Option.Factory());
41859             app.zephyr.directive('directory', app.directives.Directory.Factory());
41860         })(app || (app = {}));
41861
41862
41863 /***/ },
41864 /* 10 */
41865 /***/ function(module, exports) {
41866
41867         var app;
41868         (function (app) {
41869             var declares;
41870             (function (declares) {
41871                 var CommandInfo = (function () {
41872                     function CommandInfo(name) {
41873                         this.name = name;
41874                     }
41875                     return CommandInfo;
41876                 })();
41877                 declares.CommandInfo = CommandInfo;
41878             })(declares = app.declares || (app.declares = {}));
41879         })(app || (app = {}));
41880         var app;
41881         (function (app) {
41882             var services;
41883             (function (services) {
41884                 var APIEndPoint = (function () {
41885                     function APIEndPoint($resource) {
41886                         this.$resource = $resource;
41887                     }
41888                     APIEndPoint.prototype.resource = function (endPoint) {
41889                         var customAction = {
41890                             method: 'GET',
41891                             isArray: false
41892                         };
41893                         return this.$resource(endPoint, {}, { customAction: customAction });
41894                     };
41895                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41896                         var endPoint = '/api/optionControlFile/' + command;
41897                         return this.resource(endPoint).get();
41898                     };
41899                     APIEndPoint.prototype.getFiles = function (fileId) {
41900                         var endPoint = '/api/v1/workspace';
41901                         if (fileId) {
41902                             endPoint += '/' + fileId;
41903                         }
41904                         return this.resource(endPoint).get();
41905                     };
41906                     APIEndPoint.prototype.getDirectories = function () {
41907                         var endPoint = '/api/v1/all/workspace/directory';
41908                         return this.resource(endPoint).get();
41909                     };
41910                     return APIEndPoint;
41911                 })();
41912                 services.APIEndPoint = APIEndPoint;
41913             })(services = app.services || (app.services = {}));
41914         })(app || (app = {}));
41915         var app;
41916         (function (app) {
41917             var services;
41918             (function (services) {
41919                 var MyModal = (function () {
41920                     function MyModal($uibModal) {
41921                         this.$uibModal = $uibModal;
41922                         this.modalOption = {
41923                             backdrop: true,
41924                             controller: null,
41925                             templateUrl: null,
41926                             size: null
41927                         };
41928                     }
41929                     MyModal.prototype.open = function (modalName) {
41930                         if (modalName === 'SelectCommand') {
41931                             this.modalOption.templateUrl = 'templates/select-command.html';
41932                             this.modalOption.size = 'lg';
41933                         }
41934                         return this.$uibModal.open(this.modalOption);
41935                     };
41936                     return MyModal;
41937                 })();
41938                 services.MyModal = MyModal;
41939             })(services = app.services || (app.services = {}));
41940         })(app || (app = {}));
41941         var app;
41942         (function (app) {
41943             var directives;
41944             (function (directives) {
41945                 var Command = (function () {
41946                     function Command() {
41947                         this.restrict = 'E';
41948                         this.replace = true;
41949                         this.scope = true;
41950                         this.controller = 'commandController';
41951                         this.controllerAs = 'ctrl';
41952                         this.bindToController = {
41953                             index: '=',
41954                             name: '=',
41955                             remove: '&',
41956                             list: '='
41957                         };
41958                         this.templateUrl = 'templates/command.html';
41959                     }
41960                     Command.Factory = function () {
41961                         var directive = function () {
41962                             return new Command();
41963                         };
41964                         directive.$inject = [];
41965                         return directive;
41966                     };
41967                     return Command;
41968                 })();
41969                 directives.Command = Command;
41970                 var CommandController = (function () {
41971                     function CommandController(APIEndPoint, $scope) {
41972                         this.APIEndPoint = APIEndPoint;
41973                         this.$scope = $scope;
41974                         var controller = this;
41975                         this.APIEndPoint
41976                             .getOptionControlFile('dcdFilePrint')
41977                             .$promise
41978                             .then(function (result) {
41979                             controller.options = result.info;
41980                         });
41981                         this.APIEndPoint
41982                             .getDirectories()
41983                             .$promise
41984                             .then(function (result) {
41985                             controller.dirs = result.info;
41986                         });
41987                         this.heading = "[" + this.index + "]: dcdFilePring";
41988                         this.isOpen = true;
41989                         this.$scope.$on('close', function () {
41990                             controller.isOpen = false;
41991                         });
41992                     }
41993                     CommandController.prototype.submit = function () {
41994                         var opt = [];
41995                         angular.forEach(this.options, function (option) {
41996                             var obj = {
41997                                 name: option.option,
41998                                 arguments: []
41999                             };
42000                             angular.forEach(option.arg, function (arg) {
42001                                 if (arg.input) {
42002                                     obj.arguments.push(arg.input);
42003                                 }
42004                             });
42005                             if (obj.arguments.length > 0) {
42006                                 opt.push(obj);
42007                             }
42008                         });
42009                         var execObj = {
42010                             command: this.name,
42011                             workspace: this.workspace.fileId,
42012                             option: opt
42013                         };
42014                         console.log(JSON.stringify(execObj, null, '\t'));
42015                     };
42016                     CommandController.prototype.removeMySelf = function (index) {
42017                         this.remove()(index, this.list);
42018                     };
42019                     CommandController.prototype.reloadFiles = function () {
42020                         var _this = this;
42021                         var fileId = this.workspace.fileId;
42022                         this.APIEndPoint
42023                             .getFiles(fileId)
42024                             .$promise
42025                             .then(function (result) {
42026                             var status = result.status;
42027                             if (status === 'success') {
42028                                 _this.files = result.info;
42029                             }
42030                             else {
42031                                 console.log(result.message);
42032                             }
42033                         });
42034                     };
42035                     CommandController.prototype.debug = function () {
42036                         console.log(this.files);
42037                         console.log(this.files);
42038                         console.log(this.workspace);
42039                     };
42040                     CommandController.$inject = ['APIEndPoint', '$scope'];
42041                     return CommandController;
42042                 })();
42043                 directives.CommandController = CommandController;
42044             })(directives = app.directives || (app.directives = {}));
42045         })(app || (app = {}));
42046         var app;
42047         (function (app) {
42048             var directives;
42049             (function (directives) {
42050                 var HeaderMenu = (function () {
42051                     function HeaderMenu() {
42052                         this.restrict = 'E';
42053                         this.replace = true;
42054                         this.templateUrl = 'templates/header-menu.html';
42055                     }
42056                     HeaderMenu.Factory = function () {
42057                         var directive = function () {
42058                             return new HeaderMenu();
42059                         };
42060                         return directive;
42061                     };
42062                     return HeaderMenu;
42063                 })();
42064                 directives.HeaderMenu = HeaderMenu;
42065             })(directives = app.directives || (app.directives = {}));
42066         })(app || (app = {}));
42067         var app;
42068         (function (app) {
42069             var directives;
42070             (function (directives) {
42071                 var Option = (function () {
42072                     function Option() {
42073                         this.restrict = 'E';
42074                         this.replace = true;
42075                         this.controller = 'optionController';
42076                         this.bindToController = {
42077                             info: '=',
42078                             files: '='
42079                         };
42080                         this.scope = true;
42081                         this.templateUrl = 'templates/option.html';
42082                         this.controllerAs = 'ctrl';
42083                     }
42084                     Option.Factory = function () {
42085                         var directive = function () {
42086                             return new Option();
42087                         };
42088                         directive.$inject = [];
42089                         return directive;
42090                     };
42091                     return Option;
42092                 })();
42093                 directives.Option = Option;
42094                 var OptionController = (function () {
42095                     function OptionController() {
42096                         var controller = this;
42097                         angular.forEach(controller.info.arg, function (arg) {
42098                             if (arg.initialValue) {
42099                                 if (arg.formType === 'number') {
42100                                     arg.input = parseInt(arg.initialValue);
42101                                 }
42102                                 else {
42103                                     arg.input = arg.initialValue;
42104                                 }
42105                             }
42106                         });
42107                     }
42108                     OptionController.$inject = [];
42109                     return OptionController;
42110                 })();
42111                 directives.OptionController = OptionController;
42112             })(directives = app.directives || (app.directives = {}));
42113         })(app || (app = {}));
42114         var app;
42115         (function (app) {
42116             var directives;
42117             (function (directives) {
42118                 var Directory = (function () {
42119                     function Directory() {
42120                         this.restrict = 'E';
42121                         this.replace = true;
42122                         this.controller = 'directoryController';
42123                         this.controllerAs = 'ctrl';
42124                         this.bindToController = {
42125                             info: '=',
42126                             add: '&',
42127                             list: '=',
42128                             files: '='
42129                         };
42130                         this.templateUrl = 'templates/directory.html';
42131                     }
42132                     Directory.Factory = function () {
42133                         var directive = function () {
42134                             return new Directory();
42135                         };
42136                         return directive;
42137                     };
42138                     return Directory;
42139                 })();
42140                 directives.Directory = Directory;
42141                 var DirectoryController = (function () {
42142                     function DirectoryController(APIEndPoint, $scope) {
42143                         this.APIEndPoint = APIEndPoint;
42144                         this.$scope = $scope;
42145                         var controller = this;
42146                         this.APIEndPoint
42147                             .getFiles(this.info.fileId)
42148                             .$promise
42149                             .then(function (result) {
42150                             if (result.status === 'success') {
42151                                 controller.files = result.info;
42152                                 angular.forEach(result.info, function (file) {
42153                                     if (file.fileType === '0') {
42154                                         var o = file;
42155                                         if (controller.info.path === '/') {
42156                                             o.path = '/' + file.name;
42157                                         }
42158                                         else {
42159                                             o.path = controller.info.path + '/' + file.name;
42160                                         }
42161                                         controller.add()(o, controller.list);
42162                                     }
42163                                 });
42164                             }
42165                             ;
42166                         });
42167                     }
42168                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
42169                     return DirectoryController;
42170                 })();
42171                 directives.DirectoryController = DirectoryController;
42172             })(directives = app.directives || (app.directives = {}));
42173         })(app || (app = {}));
42174         var app;
42175         (function (app) {
42176             var controllers;
42177             (function (controllers) {
42178                 var Execution = (function () {
42179                     function Execution(MyModal, $scope) {
42180                         this.MyModal = MyModal;
42181                         this.$scope = $scope;
42182                         this.commandInfoList = [];
42183                     }
42184                     ;
42185                     Execution.prototype.add = function () {
42186                         this.$scope.$broadcast('close');
42187                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
42188                     };
42189                     Execution.prototype.open = function () {
42190                         var result = this.MyModal.open('SelectCommand');
42191                         console.log(result);
42192                     };
42193                     Execution.prototype.remove = function (index, list) {
42194                         list.splice(index, 1);
42195                     };
42196                     Execution.prototype.close = function () {
42197                         console.log("close");
42198                     };
42199                     Execution.$inject = ['MyModal', '$scope'];
42200                     return Execution;
42201                 })();
42202                 controllers.Execution = Execution;
42203             })(controllers = app.controllers || (app.controllers = {}));
42204         })(app || (app = {}));
42205         var app;
42206         (function (app) {
42207             var controllers;
42208             (function (controllers) {
42209                 var Workspace = (function () {
42210                     function Workspace($scope, APIEndPoint) {
42211                         this.$scope = $scope;
42212                         this.APIEndPoint = APIEndPoint;
42213                         this.directoryList = [];
42214                         var controller = this;
42215                         var directoryList = this.directoryList;
42216                         var o = {
42217                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
42218                             name: '',
42219                             parentId: '',
42220                             fileType: '',
42221                             createdAt: '',
42222                             updatedAt: '',
42223                             path: '/'
42224                         };
42225                         directoryList.push(o);
42226                     }
42227                     Workspace.prototype.addDirectory = function (info, directoryList) {
42228                         directoryList.push(info);
42229                     };
42230                     Workspace.$inject = ['$scope', 'APIEndPoint'];
42231                     return Workspace;
42232                 })();
42233                 controllers.Workspace = Workspace;
42234             })(controllers = app.controllers || (app.controllers = {}));
42235         })(app || (app = {}));
42236         var app;
42237         (function (app) {
42238             var controllers;
42239             (function (controllers) {
42240                 var History = (function () {
42241                     function History($scope) {
42242                         this.page = "History";
42243                     }
42244                     History.$inject = ['$scope'];
42245                     return History;
42246                 })();
42247                 controllers.History = History;
42248             })(controllers = app.controllers || (app.controllers = {}));
42249         })(app || (app = {}));
42250         var app;
42251         (function (app) {
42252             'use strict';
42253             var appName = 'zephyr';
42254             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42255             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42256                 $urlRouterProvider.otherwise('/execution');
42257                 $locationProvider.html5Mode({
42258                     enabled: true,
42259                     requireBase: false
42260                 });
42261                 $stateProvider
42262                     .state('execution', {
42263                     url: '/execution',
42264                     templateUrl: 'templates/execution.html',
42265                     controller: 'executionController',
42266                     controllerAs: 'c'
42267                 })
42268                     .state('workspace', {
42269                     url: '/workspace',
42270                     templateUrl: 'templates/workspace.html',
42271                     controller: 'workspaceController',
42272                     controllerAs: 'c'
42273                 })
42274                     .state('history', {
42275                     url: '/history',
42276                     templateUrl: 'templates/history.html',
42277                     controller: 'historyController',
42278                     controllerAs: 'c'
42279                 });
42280             });
42281             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42282             app.zephyr.service('MyModal', app.services.MyModal);
42283             app.zephyr.controller('executionController', app.controllers.Execution);
42284             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42285             app.zephyr.controller('historyController', app.controllers.History);
42286             app.zephyr.controller('commandController', app.directives.CommandController);
42287             app.zephyr.controller('optionController', app.directives.OptionController);
42288             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42289             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42290             app.zephyr.directive('command', app.directives.Command.Factory());
42291             app.zephyr.directive('option', app.directives.Option.Factory());
42292             app.zephyr.directive('directory', app.directives.Directory.Factory());
42293         })(app || (app = {}));
42294
42295
42296 /***/ },
42297 /* 11 */
42298 /***/ function(module, exports) {
42299
42300         var app;
42301         (function (app) {
42302             var declares;
42303             (function (declares) {
42304                 var CommandInfo = (function () {
42305                     function CommandInfo(name) {
42306                         this.name = name;
42307                     }
42308                     return CommandInfo;
42309                 })();
42310                 declares.CommandInfo = CommandInfo;
42311             })(declares = app.declares || (app.declares = {}));
42312         })(app || (app = {}));
42313         var app;
42314         (function (app) {
42315             var services;
42316             (function (services) {
42317                 var APIEndPoint = (function () {
42318                     function APIEndPoint($resource) {
42319                         this.$resource = $resource;
42320                     }
42321                     APIEndPoint.prototype.resource = function (endPoint) {
42322                         var customAction = {
42323                             method: 'GET',
42324                             isArray: false
42325                         };
42326                         return this.$resource(endPoint, {}, { customAction: customAction });
42327                     };
42328                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42329                         var endPoint = '/api/optionControlFile/' + command;
42330                         return this.resource(endPoint).get();
42331                     };
42332                     APIEndPoint.prototype.getFiles = function (fileId) {
42333                         var endPoint = '/api/v1/workspace';
42334                         if (fileId) {
42335                             endPoint += '/' + fileId;
42336                         }
42337                         return this.resource(endPoint).get();
42338                     };
42339                     APIEndPoint.prototype.getDirectories = function () {
42340                         var endPoint = '/api/v1/all/workspace/directory';
42341                         return this.resource(endPoint).get();
42342                     };
42343                     return APIEndPoint;
42344                 })();
42345                 services.APIEndPoint = APIEndPoint;
42346             })(services = app.services || (app.services = {}));
42347         })(app || (app = {}));
42348         var app;
42349         (function (app) {
42350             var services;
42351             (function (services) {
42352                 var MyModal = (function () {
42353                     function MyModal($uibModal) {
42354                         this.$uibModal = $uibModal;
42355                         this.modalOption = {
42356                             backdrop: true,
42357                             controller: null,
42358                             templateUrl: null,
42359                             size: null
42360                         };
42361                     }
42362                     MyModal.prototype.open = function (modalName) {
42363                         if (modalName === 'SelectCommand') {
42364                             this.modalOption.templateUrl = 'templates/select-command.html';
42365                             this.modalOption.size = 'lg';
42366                         }
42367                         return this.$uibModal.open(this.modalOption);
42368                     };
42369                     return MyModal;
42370                 })();
42371                 services.MyModal = MyModal;
42372             })(services = app.services || (app.services = {}));
42373         })(app || (app = {}));
42374         var app;
42375         (function (app) {
42376             var directives;
42377             (function (directives) {
42378                 var Command = (function () {
42379                     function Command() {
42380                         this.restrict = 'E';
42381                         this.replace = true;
42382                         this.scope = true;
42383                         this.controller = 'commandController';
42384                         this.controllerAs = 'ctrl';
42385                         this.bindToController = {
42386                             index: '=',
42387                             name: '=',
42388                             remove: '&',
42389                             list: '='
42390                         };
42391                         this.templateUrl = 'templates/command.html';
42392                     }
42393                     Command.Factory = function () {
42394                         var directive = function () {
42395                             return new Command();
42396                         };
42397                         directive.$inject = [];
42398                         return directive;
42399                     };
42400                     return Command;
42401                 })();
42402                 directives.Command = Command;
42403                 var CommandController = (function () {
42404                     function CommandController(APIEndPoint, $scope) {
42405                         this.APIEndPoint = APIEndPoint;
42406                         this.$scope = $scope;
42407                         var controller = this;
42408                         this.APIEndPoint
42409                             .getOptionControlFile('dcdFilePrint')
42410                             .$promise
42411                             .then(function (result) {
42412                             controller.options = result.info;
42413                         });
42414                         this.APIEndPoint
42415                             .getDirectories()
42416                             .$promise
42417                             .then(function (result) {
42418                             controller.dirs = result.info;
42419                         });
42420                         this.heading = "[" + this.index + "]: dcdFilePring";
42421                         this.isOpen = true;
42422                         this.$scope.$on('close', function () {
42423                             controller.isOpen = false;
42424                         });
42425                     }
42426                     CommandController.prototype.submit = function () {
42427                         var opt = [];
42428                         angular.forEach(this.options, function (option) {
42429                             var obj = {
42430                                 name: option.option,
42431                                 arguments: []
42432                             };
42433                             angular.forEach(option.arg, function (arg) {
42434                                 if (arg.input) {
42435                                     obj.arguments.push(arg.input);
42436                                 }
42437                             });
42438                             if (obj.arguments.length > 0) {
42439                                 opt.push(obj);
42440                             }
42441                         });
42442                         var execObj = {
42443                             command: this.name,
42444                             workspace: this.workspace.fileId,
42445                             option: opt
42446                         };
42447                         console.log(JSON.stringify(execObj, null, '\t'));
42448                     };
42449                     CommandController.prototype.removeMySelf = function (index) {
42450                         this.remove()(index, this.list);
42451                     };
42452                     CommandController.prototype.reloadFiles = function () {
42453                         var _this = this;
42454                         var fileId = this.workspace.fileId;
42455                         this.APIEndPoint
42456                             .getFiles(fileId)
42457                             .$promise
42458                             .then(function (result) {
42459                             var status = result.status;
42460                             if (status === 'success') {
42461                                 _this.files = result.info;
42462                             }
42463                             else {
42464                                 console.log(result.message);
42465                             }
42466                         });
42467                     };
42468                     CommandController.prototype.debug = function () {
42469                         console.log(this.files);
42470                         console.log(this.files);
42471                         console.log(this.workspace);
42472                     };
42473                     CommandController.$inject = ['APIEndPoint', '$scope'];
42474                     return CommandController;
42475                 })();
42476                 directives.CommandController = CommandController;
42477             })(directives = app.directives || (app.directives = {}));
42478         })(app || (app = {}));
42479         var app;
42480         (function (app) {
42481             var directives;
42482             (function (directives) {
42483                 var HeaderMenu = (function () {
42484                     function HeaderMenu() {
42485                         this.restrict = 'E';
42486                         this.replace = true;
42487                         this.templateUrl = 'templates/header-menu.html';
42488                     }
42489                     HeaderMenu.Factory = function () {
42490                         var directive = function () {
42491                             return new HeaderMenu();
42492                         };
42493                         return directive;
42494                     };
42495                     return HeaderMenu;
42496                 })();
42497                 directives.HeaderMenu = HeaderMenu;
42498             })(directives = app.directives || (app.directives = {}));
42499         })(app || (app = {}));
42500         var app;
42501         (function (app) {
42502             var directives;
42503             (function (directives) {
42504                 var Option = (function () {
42505                     function Option() {
42506                         this.restrict = 'E';
42507                         this.replace = true;
42508                         this.controller = 'optionController';
42509                         this.bindToController = {
42510                             info: '=',
42511                             files: '='
42512                         };
42513                         this.scope = true;
42514                         this.templateUrl = 'templates/option.html';
42515                         this.controllerAs = 'ctrl';
42516                     }
42517                     Option.Factory = function () {
42518                         var directive = function () {
42519                             return new Option();
42520                         };
42521                         directive.$inject = [];
42522                         return directive;
42523                     };
42524                     return Option;
42525                 })();
42526                 directives.Option = Option;
42527                 var OptionController = (function () {
42528                     function OptionController() {
42529                         var controller = this;
42530                         angular.forEach(controller.info.arg, function (arg) {
42531                             if (arg.initialValue) {
42532                                 if (arg.formType === 'number') {
42533                                     arg.input = parseInt(arg.initialValue);
42534                                 }
42535                                 else {
42536                                     arg.input = arg.initialValue;
42537                                 }
42538                             }
42539                         });
42540                     }
42541                     OptionController.$inject = [];
42542                     return OptionController;
42543                 })();
42544                 directives.OptionController = OptionController;
42545             })(directives = app.directives || (app.directives = {}));
42546         })(app || (app = {}));
42547         var app;
42548         (function (app) {
42549             var directives;
42550             (function (directives) {
42551                 var Directory = (function () {
42552                     function Directory() {
42553                         this.restrict = 'E';
42554                         this.replace = true;
42555                         this.controller = 'directoryController';
42556                         this.controllerAs = 'ctrl';
42557                         this.bindToController = {
42558                             info: '=',
42559                             add: '&',
42560                             list: '=',
42561                             files: '='
42562                         };
42563                         this.templateUrl = 'templates/directory.html';
42564                     }
42565                     Directory.Factory = function () {
42566                         var directive = function () {
42567                             return new Directory();
42568                         };
42569                         return directive;
42570                     };
42571                     return Directory;
42572                 })();
42573                 directives.Directory = Directory;
42574                 var DirectoryController = (function () {
42575                     function DirectoryController(APIEndPoint, $scope) {
42576                         this.APIEndPoint = APIEndPoint;
42577                         this.$scope = $scope;
42578                         var controller = this;
42579                         this.APIEndPoint
42580                             .getFiles(this.info.fileId)
42581                             .$promise
42582                             .then(function (result) {
42583                             if (result.status === 'success') {
42584                                 controller.files = result.info;
42585                                 angular.forEach(result.info, function (file) {
42586                                     if (file.fileType === '0') {
42587                                         var o = file;
42588                                         if (controller.info.path === '/') {
42589                                             o.path = '/' + file.name;
42590                                         }
42591                                         else {
42592                                             o.path = controller.info.path + '/' + file.name;
42593                                         }
42594                                         controller.add()(o, controller.list);
42595                                     }
42596                                 });
42597                             }
42598                             ;
42599                         });
42600                     }
42601                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
42602                     return DirectoryController;
42603                 })();
42604                 directives.DirectoryController = DirectoryController;
42605             })(directives = app.directives || (app.directives = {}));
42606         })(app || (app = {}));
42607         var app;
42608         (function (app) {
42609             var controllers;
42610             (function (controllers) {
42611                 var Execution = (function () {
42612                     function Execution(MyModal, $scope) {
42613                         this.MyModal = MyModal;
42614                         this.$scope = $scope;
42615                         this.commandInfoList = [];
42616                     }
42617                     ;
42618                     Execution.prototype.add = function () {
42619                         this.$scope.$broadcast('close');
42620                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
42621                     };
42622                     Execution.prototype.open = function () {
42623                         var result = this.MyModal.open('SelectCommand');
42624                         console.log(result);
42625                     };
42626                     Execution.prototype.remove = function (index, list) {
42627                         list.splice(index, 1);
42628                     };
42629                     Execution.prototype.close = function () {
42630                         console.log("close");
42631                     };
42632                     Execution.$inject = ['MyModal', '$scope'];
42633                     return Execution;
42634                 })();
42635                 controllers.Execution = Execution;
42636             })(controllers = app.controllers || (app.controllers = {}));
42637         })(app || (app = {}));
42638         var app;
42639         (function (app) {
42640             var controllers;
42641             (function (controllers) {
42642                 var Workspace = (function () {
42643                     function Workspace($scope, APIEndPoint) {
42644                         this.$scope = $scope;
42645                         this.APIEndPoint = APIEndPoint;
42646                         this.directoryList = [];
42647                         var controller = this;
42648                         var directoryList = this.directoryList;
42649                         var o = {
42650                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
42651                             name: '',
42652                             parentId: '',
42653                             fileType: '',
42654                             createdAt: '',
42655                             updatedAt: '',
42656                             path: '/'
42657                         };
42658                         directoryList.push(o);
42659                     }
42660                     Workspace.prototype.addDirectory = function (info, directoryList) {
42661                         directoryList.push(info);
42662                     };
42663                     Workspace.$inject = ['$scope', 'APIEndPoint'];
42664                     return Workspace;
42665                 })();
42666                 controllers.Workspace = Workspace;
42667             })(controllers = app.controllers || (app.controllers = {}));
42668         })(app || (app = {}));
42669         var app;
42670         (function (app) {
42671             var controllers;
42672             (function (controllers) {
42673                 var History = (function () {
42674                     function History($scope) {
42675                         this.page = "History";
42676                     }
42677                     History.$inject = ['$scope'];
42678                     return History;
42679                 })();
42680                 controllers.History = History;
42681             })(controllers = app.controllers || (app.controllers = {}));
42682         })(app || (app = {}));
42683         var app;
42684         (function (app) {
42685             'use strict';
42686             var appName = 'zephyr';
42687             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42688             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42689                 $urlRouterProvider.otherwise('/execution');
42690                 $locationProvider.html5Mode({
42691                     enabled: true,
42692                     requireBase: false
42693                 });
42694                 $stateProvider
42695                     .state('execution', {
42696                     url: '/execution',
42697                     templateUrl: 'templates/execution.html',
42698                     controller: 'executionController',
42699                     controllerAs: 'c'
42700                 })
42701                     .state('workspace', {
42702                     url: '/workspace',
42703                     templateUrl: 'templates/workspace.html',
42704                     controller: 'workspaceController',
42705                     controllerAs: 'c'
42706                 })
42707                     .state('history', {
42708                     url: '/history',
42709                     templateUrl: 'templates/history.html',
42710                     controller: 'historyController',
42711                     controllerAs: 'c'
42712                 });
42713             });
42714             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42715             app.zephyr.service('MyModal', app.services.MyModal);
42716             app.zephyr.controller('executionController', app.controllers.Execution);
42717             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42718             app.zephyr.controller('historyController', app.controllers.History);
42719             app.zephyr.controller('commandController', app.directives.CommandController);
42720             app.zephyr.controller('optionController', app.directives.OptionController);
42721             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42722             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42723             app.zephyr.directive('command', app.directives.Command.Factory());
42724             app.zephyr.directive('option', app.directives.Option.Factory());
42725             app.zephyr.directive('directory', app.directives.Directory.Factory());
42726         })(app || (app = {}));
42727
42728
42729 /***/ },
42730 /* 12 */
42731 /***/ function(module, exports) {
42732
42733         var app;
42734         (function (app) {
42735             var declares;
42736             (function (declares) {
42737                 var CommandInfo = (function () {
42738                     function CommandInfo(name) {
42739                         this.name = name;
42740                     }
42741                     return CommandInfo;
42742                 })();
42743                 declares.CommandInfo = CommandInfo;
42744             })(declares = app.declares || (app.declares = {}));
42745         })(app || (app = {}));
42746         var app;
42747         (function (app) {
42748             var services;
42749             (function (services) {
42750                 var APIEndPoint = (function () {
42751                     function APIEndPoint($resource) {
42752                         this.$resource = $resource;
42753                     }
42754                     APIEndPoint.prototype.resource = function (endPoint) {
42755                         var customAction = {
42756                             method: 'GET',
42757                             isArray: false
42758                         };
42759                         return this.$resource(endPoint, {}, { customAction: customAction });
42760                     };
42761                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42762                         var endPoint = '/api/optionControlFile/' + command;
42763                         return this.resource(endPoint).get();
42764                     };
42765                     APIEndPoint.prototype.getFiles = function (fileId) {
42766                         var endPoint = '/api/v1/workspace';
42767                         if (fileId) {
42768                             endPoint += '/' + fileId;
42769                         }
42770                         return this.resource(endPoint).get();
42771                     };
42772                     APIEndPoint.prototype.getDirectories = function () {
42773                         var endPoint = '/api/v1/all/workspace/directory';
42774                         return this.resource(endPoint).get();
42775                     };
42776                     return APIEndPoint;
42777                 })();
42778                 services.APIEndPoint = APIEndPoint;
42779             })(services = app.services || (app.services = {}));
42780         })(app || (app = {}));
42781         var app;
42782         (function (app) {
42783             var services;
42784             (function (services) {
42785                 var MyModal = (function () {
42786                     function MyModal($uibModal) {
42787                         this.$uibModal = $uibModal;
42788                         this.modalOption = {
42789                             backdrop: true,
42790                             controller: null,
42791                             templateUrl: null,
42792                             size: null
42793                         };
42794                     }
42795                     MyModal.prototype.open = function (modalName) {
42796                         if (modalName === 'SelectCommand') {
42797                             this.modalOption.templateUrl = 'templates/select-command.html';
42798                             this.modalOption.size = 'lg';
42799                         }
42800                         return this.$uibModal.open(this.modalOption);
42801                     };
42802                     return MyModal;
42803                 })();
42804                 services.MyModal = MyModal;
42805             })(services = app.services || (app.services = {}));
42806         })(app || (app = {}));
42807         var app;
42808         (function (app) {
42809             var directives;
42810             (function (directives) {
42811                 var Command = (function () {
42812                     function Command() {
42813                         this.restrict = 'E';
42814                         this.replace = true;
42815                         this.scope = true;
42816                         this.controller = 'commandController';
42817                         this.controllerAs = 'ctrl';
42818                         this.bindToController = {
42819                             index: '=',
42820                             name: '=',
42821                             remove: '&',
42822                             list: '='
42823                         };
42824                         this.templateUrl = 'templates/command.html';
42825                     }
42826                     Command.Factory = function () {
42827                         var directive = function () {
42828                             return new Command();
42829                         };
42830                         directive.$inject = [];
42831                         return directive;
42832                     };
42833                     return Command;
42834                 })();
42835                 directives.Command = Command;
42836                 var CommandController = (function () {
42837                     function CommandController(APIEndPoint, $scope) {
42838                         this.APIEndPoint = APIEndPoint;
42839                         this.$scope = $scope;
42840                         var controller = this;
42841                         this.APIEndPoint
42842                             .getOptionControlFile('dcdFilePrint')
42843                             .$promise
42844                             .then(function (result) {
42845                             controller.options = result.info;
42846                         });
42847                         this.APIEndPoint
42848                             .getDirectories()
42849                             .$promise
42850                             .then(function (result) {
42851                             controller.dirs = result.info;
42852                         });
42853                         this.heading = "[" + this.index + "]: dcdFilePring";
42854                         this.isOpen = true;
42855                         this.$scope.$on('close', function () {
42856                             controller.isOpen = false;
42857                         });
42858                     }
42859                     CommandController.prototype.submit = function () {
42860                         var opt = [];
42861                         angular.forEach(this.options, function (option) {
42862                             var obj = {
42863                                 name: option.option,
42864                                 arguments: []
42865                             };
42866                             angular.forEach(option.arg, function (arg) {
42867                                 if (arg.input) {
42868                                     obj.arguments.push(arg.input);
42869                                 }
42870                             });
42871                             if (obj.arguments.length > 0) {
42872                                 opt.push(obj);
42873                             }
42874                         });
42875                         var execObj = {
42876                             command: this.name,
42877                             workspace: this.workspace.fileId,
42878                             option: opt
42879                         };
42880                         console.log(JSON.stringify(execObj, null, '\t'));
42881                     };
42882                     CommandController.prototype.removeMySelf = function (index) {
42883                         this.remove()(index, this.list);
42884                     };
42885                     CommandController.prototype.reloadFiles = function () {
42886                         var _this = this;
42887                         var fileId = this.workspace.fileId;
42888                         this.APIEndPoint
42889                             .getFiles(fileId)
42890                             .$promise
42891                             .then(function (result) {
42892                             var status = result.status;
42893                             if (status === 'success') {
42894                                 _this.files = result.info;
42895                             }
42896                             else {
42897                                 console.log(result.message);
42898                             }
42899                         });
42900                     };
42901                     CommandController.prototype.debug = function () {
42902                         console.log(this.files);
42903                         console.log(this.files);
42904                         console.log(this.workspace);
42905                     };
42906                     CommandController.$inject = ['APIEndPoint', '$scope'];
42907                     return CommandController;
42908                 })();
42909                 directives.CommandController = CommandController;
42910             })(directives = app.directives || (app.directives = {}));
42911         })(app || (app = {}));
42912         var app;
42913         (function (app) {
42914             var directives;
42915             (function (directives) {
42916                 var HeaderMenu = (function () {
42917                     function HeaderMenu() {
42918                         this.restrict = 'E';
42919                         this.replace = true;
42920                         this.templateUrl = 'templates/header-menu.html';
42921                     }
42922                     HeaderMenu.Factory = function () {
42923                         var directive = function () {
42924                             return new HeaderMenu();
42925                         };
42926                         return directive;
42927                     };
42928                     return HeaderMenu;
42929                 })();
42930                 directives.HeaderMenu = HeaderMenu;
42931             })(directives = app.directives || (app.directives = {}));
42932         })(app || (app = {}));
42933         var app;
42934         (function (app) {
42935             var directives;
42936             (function (directives) {
42937                 var Option = (function () {
42938                     function Option() {
42939                         this.restrict = 'E';
42940                         this.replace = true;
42941                         this.controller = 'optionController';
42942                         this.bindToController = {
42943                             info: '=',
42944                             files: '='
42945                         };
42946                         this.scope = true;
42947                         this.templateUrl = 'templates/option.html';
42948                         this.controllerAs = 'ctrl';
42949                     }
42950                     Option.Factory = function () {
42951                         var directive = function () {
42952                             return new Option();
42953                         };
42954                         directive.$inject = [];
42955                         return directive;
42956                     };
42957                     return Option;
42958                 })();
42959                 directives.Option = Option;
42960                 var OptionController = (function () {
42961                     function OptionController() {
42962                         var controller = this;
42963                         angular.forEach(controller.info.arg, function (arg) {
42964                             if (arg.initialValue) {
42965                                 if (arg.formType === 'number') {
42966                                     arg.input = parseInt(arg.initialValue);
42967                                 }
42968                                 else {
42969                                     arg.input = arg.initialValue;
42970                                 }
42971                             }
42972                         });
42973                     }
42974                     OptionController.$inject = [];
42975                     return OptionController;
42976                 })();
42977                 directives.OptionController = OptionController;
42978             })(directives = app.directives || (app.directives = {}));
42979         })(app || (app = {}));
42980         var app;
42981         (function (app) {
42982             var directives;
42983             (function (directives) {
42984                 var Directory = (function () {
42985                     function Directory() {
42986                         this.restrict = 'E';
42987                         this.replace = true;
42988                         this.controller = 'directoryController';
42989                         this.controllerAs = 'ctrl';
42990                         this.bindToController = {
42991                             info: '=',
42992                             add: '&',
42993                             list: '=',
42994                             files: '='
42995                         };
42996                         this.templateUrl = 'templates/directory.html';
42997                     }
42998                     Directory.Factory = function () {
42999                         var directive = function () {
43000                             return new Directory();
43001                         };
43002                         return directive;
43003                     };
43004                     return Directory;
43005                 })();
43006                 directives.Directory = Directory;
43007                 var DirectoryController = (function () {
43008                     function DirectoryController(APIEndPoint, $scope) {
43009                         this.APIEndPoint = APIEndPoint;
43010                         this.$scope = $scope;
43011                         var controller = this;
43012                         this.APIEndPoint
43013                             .getFiles(this.info.fileId)
43014                             .$promise
43015                             .then(function (result) {
43016                             if (result.status === 'success') {
43017                                 controller.files = result.info;
43018                                 angular.forEach(result.info, function (file) {
43019                                     if (file.fileType === '0') {
43020                                         var o = file;
43021                                         if (controller.info.path === '/') {
43022                                             o.path = '/' + file.name;
43023                                         }
43024                                         else {
43025                                             o.path = controller.info.path + '/' + file.name;
43026                                         }
43027                                         controller.add()(o, controller.list);
43028                                     }
43029                                 });
43030                             }
43031                             ;
43032                         });
43033                     }
43034                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43035                     return DirectoryController;
43036                 })();
43037                 directives.DirectoryController = DirectoryController;
43038             })(directives = app.directives || (app.directives = {}));
43039         })(app || (app = {}));
43040         var app;
43041         (function (app) {
43042             var controllers;
43043             (function (controllers) {
43044                 var Execution = (function () {
43045                     function Execution(MyModal, $scope) {
43046                         this.MyModal = MyModal;
43047                         this.$scope = $scope;
43048                         this.commandInfoList = [];
43049                     }
43050                     ;
43051                     Execution.prototype.add = function () {
43052                         this.$scope.$broadcast('close');
43053                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43054                     };
43055                     Execution.prototype.open = function () {
43056                         var result = this.MyModal.open('SelectCommand');
43057                         console.log(result);
43058                     };
43059                     Execution.prototype.remove = function (index, list) {
43060                         list.splice(index, 1);
43061                     };
43062                     Execution.prototype.close = function () {
43063                         console.log("close");
43064                     };
43065                     Execution.$inject = ['MyModal', '$scope'];
43066                     return Execution;
43067                 })();
43068                 controllers.Execution = Execution;
43069             })(controllers = app.controllers || (app.controllers = {}));
43070         })(app || (app = {}));
43071         var app;
43072         (function (app) {
43073             var controllers;
43074             (function (controllers) {
43075                 var Workspace = (function () {
43076                     function Workspace($scope, APIEndPoint) {
43077                         this.$scope = $scope;
43078                         this.APIEndPoint = APIEndPoint;
43079                         this.directoryList = [];
43080                         var controller = this;
43081                         var directoryList = this.directoryList;
43082                         var o = {
43083                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43084                             name: '',
43085                             parentId: '',
43086                             fileType: '',
43087                             createdAt: '',
43088                             updatedAt: '',
43089                             path: '/'
43090                         };
43091                         directoryList.push(o);
43092                     }
43093                     Workspace.prototype.addDirectory = function (info, directoryList) {
43094                         directoryList.push(info);
43095                     };
43096                     Workspace.$inject = ['$scope', 'APIEndPoint'];
43097                     return Workspace;
43098                 })();
43099                 controllers.Workspace = Workspace;
43100             })(controllers = app.controllers || (app.controllers = {}));
43101         })(app || (app = {}));
43102         var app;
43103         (function (app) {
43104             var controllers;
43105             (function (controllers) {
43106                 var History = (function () {
43107                     function History($scope) {
43108                         this.page = "History";
43109                     }
43110                     History.$inject = ['$scope'];
43111                     return History;
43112                 })();
43113                 controllers.History = History;
43114             })(controllers = app.controllers || (app.controllers = {}));
43115         })(app || (app = {}));
43116         var app;
43117         (function (app) {
43118             'use strict';
43119             var appName = 'zephyr';
43120             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43121             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43122                 $urlRouterProvider.otherwise('/execution');
43123                 $locationProvider.html5Mode({
43124                     enabled: true,
43125                     requireBase: false
43126                 });
43127                 $stateProvider
43128                     .state('execution', {
43129                     url: '/execution',
43130                     templateUrl: 'templates/execution.html',
43131                     controller: 'executionController',
43132                     controllerAs: 'c'
43133                 })
43134                     .state('workspace', {
43135                     url: '/workspace',
43136                     templateUrl: 'templates/workspace.html',
43137                     controller: 'workspaceController',
43138                     controllerAs: 'c'
43139                 })
43140                     .state('history', {
43141                     url: '/history',
43142                     templateUrl: 'templates/history.html',
43143                     controller: 'historyController',
43144                     controllerAs: 'c'
43145                 });
43146             });
43147             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43148             app.zephyr.service('MyModal', app.services.MyModal);
43149             app.zephyr.controller('executionController', app.controllers.Execution);
43150             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43151             app.zephyr.controller('historyController', app.controllers.History);
43152             app.zephyr.controller('commandController', app.directives.CommandController);
43153             app.zephyr.controller('optionController', app.directives.OptionController);
43154             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43155             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43156             app.zephyr.directive('command', app.directives.Command.Factory());
43157             app.zephyr.directive('option', app.directives.Option.Factory());
43158             app.zephyr.directive('directory', app.directives.Directory.Factory());
43159         })(app || (app = {}));
43160
43161
43162 /***/ },
43163 /* 13 */
43164 /***/ function(module, exports) {
43165
43166         var app;
43167         (function (app) {
43168             var declares;
43169             (function (declares) {
43170                 var CommandInfo = (function () {
43171                     function CommandInfo(name) {
43172                         this.name = name;
43173                     }
43174                     return CommandInfo;
43175                 })();
43176                 declares.CommandInfo = CommandInfo;
43177             })(declares = app.declares || (app.declares = {}));
43178         })(app || (app = {}));
43179         var app;
43180         (function (app) {
43181             var services;
43182             (function (services) {
43183                 var APIEndPoint = (function () {
43184                     function APIEndPoint($resource) {
43185                         this.$resource = $resource;
43186                     }
43187                     APIEndPoint.prototype.resource = function (endPoint) {
43188                         var customAction = {
43189                             method: 'GET',
43190                             isArray: false
43191                         };
43192                         return this.$resource(endPoint, {}, { customAction: customAction });
43193                     };
43194                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43195                         var endPoint = '/api/optionControlFile/' + command;
43196                         return this.resource(endPoint).get();
43197                     };
43198                     APIEndPoint.prototype.getFiles = function (fileId) {
43199                         var endPoint = '/api/v1/workspace';
43200                         if (fileId) {
43201                             endPoint += '/' + fileId;
43202                         }
43203                         return this.resource(endPoint).get();
43204                     };
43205                     APIEndPoint.prototype.getDirectories = function () {
43206                         var endPoint = '/api/v1/all/workspace/directory';
43207                         return this.resource(endPoint).get();
43208                     };
43209                     return APIEndPoint;
43210                 })();
43211                 services.APIEndPoint = APIEndPoint;
43212             })(services = app.services || (app.services = {}));
43213         })(app || (app = {}));
43214         var app;
43215         (function (app) {
43216             var services;
43217             (function (services) {
43218                 var MyModal = (function () {
43219                     function MyModal($uibModal) {
43220                         this.$uibModal = $uibModal;
43221                         this.modalOption = {
43222                             backdrop: true,
43223                             controller: null,
43224                             templateUrl: null,
43225                             size: null
43226                         };
43227                     }
43228                     MyModal.prototype.open = function (modalName) {
43229                         if (modalName === 'SelectCommand') {
43230                             this.modalOption.templateUrl = 'templates/select-command.html';
43231                             this.modalOption.size = 'lg';
43232                         }
43233                         return this.$uibModal.open(this.modalOption);
43234                     };
43235                     return MyModal;
43236                 })();
43237                 services.MyModal = MyModal;
43238             })(services = app.services || (app.services = {}));
43239         })(app || (app = {}));
43240         var app;
43241         (function (app) {
43242             var directives;
43243             (function (directives) {
43244                 var Command = (function () {
43245                     function Command() {
43246                         this.restrict = 'E';
43247                         this.replace = true;
43248                         this.scope = true;
43249                         this.controller = 'commandController';
43250                         this.controllerAs = 'ctrl';
43251                         this.bindToController = {
43252                             index: '=',
43253                             name: '=',
43254                             remove: '&',
43255                             list: '='
43256                         };
43257                         this.templateUrl = 'templates/command.html';
43258                     }
43259                     Command.Factory = function () {
43260                         var directive = function () {
43261                             return new Command();
43262                         };
43263                         directive.$inject = [];
43264                         return directive;
43265                     };
43266                     return Command;
43267                 })();
43268                 directives.Command = Command;
43269                 var CommandController = (function () {
43270                     function CommandController(APIEndPoint, $scope) {
43271                         this.APIEndPoint = APIEndPoint;
43272                         this.$scope = $scope;
43273                         var controller = this;
43274                         this.APIEndPoint
43275                             .getOptionControlFile('dcdFilePrint')
43276                             .$promise
43277                             .then(function (result) {
43278                             controller.options = result.info;
43279                         });
43280                         this.APIEndPoint
43281                             .getDirectories()
43282                             .$promise
43283                             .then(function (result) {
43284                             controller.dirs = result.info;
43285                         });
43286                         this.heading = "[" + this.index + "]: dcdFilePring";
43287                         this.isOpen = true;
43288                         this.$scope.$on('close', function () {
43289                             controller.isOpen = false;
43290                         });
43291                     }
43292                     CommandController.prototype.submit = function () {
43293                         var opt = [];
43294                         angular.forEach(this.options, function (option) {
43295                             var obj = {
43296                                 name: option.option,
43297                                 arguments: []
43298                             };
43299                             angular.forEach(option.arg, function (arg) {
43300                                 if (arg.input) {
43301                                     obj.arguments.push(arg.input);
43302                                 }
43303                             });
43304                             if (obj.arguments.length > 0) {
43305                                 opt.push(obj);
43306                             }
43307                         });
43308                         var execObj = {
43309                             command: this.name,
43310                             workspace: this.workspace.fileId,
43311                             option: opt
43312                         };
43313                         console.log(JSON.stringify(execObj, null, '\t'));
43314                     };
43315                     CommandController.prototype.removeMySelf = function (index) {
43316                         this.remove()(index, this.list);
43317                     };
43318                     CommandController.prototype.reloadFiles = function () {
43319                         var _this = this;
43320                         var fileId = this.workspace.fileId;
43321                         this.APIEndPoint
43322                             .getFiles(fileId)
43323                             .$promise
43324                             .then(function (result) {
43325                             var status = result.status;
43326                             if (status === 'success') {
43327                                 _this.files = result.info;
43328                             }
43329                             else {
43330                                 console.log(result.message);
43331                             }
43332                         });
43333                     };
43334                     CommandController.prototype.debug = function () {
43335                         console.log(this.files);
43336                         console.log(this.files);
43337                         console.log(this.workspace);
43338                     };
43339                     CommandController.$inject = ['APIEndPoint', '$scope'];
43340                     return CommandController;
43341                 })();
43342                 directives.CommandController = CommandController;
43343             })(directives = app.directives || (app.directives = {}));
43344         })(app || (app = {}));
43345         var app;
43346         (function (app) {
43347             var directives;
43348             (function (directives) {
43349                 var HeaderMenu = (function () {
43350                     function HeaderMenu() {
43351                         this.restrict = 'E';
43352                         this.replace = true;
43353                         this.templateUrl = 'templates/header-menu.html';
43354                     }
43355                     HeaderMenu.Factory = function () {
43356                         var directive = function () {
43357                             return new HeaderMenu();
43358                         };
43359                         return directive;
43360                     };
43361                     return HeaderMenu;
43362                 })();
43363                 directives.HeaderMenu = HeaderMenu;
43364             })(directives = app.directives || (app.directives = {}));
43365         })(app || (app = {}));
43366         var app;
43367         (function (app) {
43368             var directives;
43369             (function (directives) {
43370                 var Option = (function () {
43371                     function Option() {
43372                         this.restrict = 'E';
43373                         this.replace = true;
43374                         this.controller = 'optionController';
43375                         this.bindToController = {
43376                             info: '=',
43377                             files: '='
43378                         };
43379                         this.scope = true;
43380                         this.templateUrl = 'templates/option.html';
43381                         this.controllerAs = 'ctrl';
43382                     }
43383                     Option.Factory = function () {
43384                         var directive = function () {
43385                             return new Option();
43386                         };
43387                         directive.$inject = [];
43388                         return directive;
43389                     };
43390                     return Option;
43391                 })();
43392                 directives.Option = Option;
43393                 var OptionController = (function () {
43394                     function OptionController() {
43395                         var controller = this;
43396                         angular.forEach(controller.info.arg, function (arg) {
43397                             if (arg.initialValue) {
43398                                 if (arg.formType === 'number') {
43399                                     arg.input = parseInt(arg.initialValue);
43400                                 }
43401                                 else {
43402                                     arg.input = arg.initialValue;
43403                                 }
43404                             }
43405                         });
43406                     }
43407                     OptionController.$inject = [];
43408                     return OptionController;
43409                 })();
43410                 directives.OptionController = OptionController;
43411             })(directives = app.directives || (app.directives = {}));
43412         })(app || (app = {}));
43413         var app;
43414         (function (app) {
43415             var directives;
43416             (function (directives) {
43417                 var Directory = (function () {
43418                     function Directory() {
43419                         this.restrict = 'E';
43420                         this.replace = true;
43421                         this.controller = 'directoryController';
43422                         this.controllerAs = 'ctrl';
43423                         this.bindToController = {
43424                             info: '=',
43425                             add: '&',
43426                             list: '=',
43427                             files: '='
43428                         };
43429                         this.templateUrl = 'templates/directory.html';
43430                     }
43431                     Directory.Factory = function () {
43432                         var directive = function () {
43433                             return new Directory();
43434                         };
43435                         return directive;
43436                     };
43437                     return Directory;
43438                 })();
43439                 directives.Directory = Directory;
43440                 var DirectoryController = (function () {
43441                     function DirectoryController(APIEndPoint, $scope) {
43442                         this.APIEndPoint = APIEndPoint;
43443                         this.$scope = $scope;
43444                         var controller = this;
43445                         this.APIEndPoint
43446                             .getFiles(this.info.fileId)
43447                             .$promise
43448                             .then(function (result) {
43449                             if (result.status === 'success') {
43450                                 controller.files = result.info;
43451                                 angular.forEach(result.info, function (file) {
43452                                     if (file.fileType === '0') {
43453                                         var o = file;
43454                                         if (controller.info.path === '/') {
43455                                             o.path = '/' + file.name;
43456                                         }
43457                                         else {
43458                                             o.path = controller.info.path + '/' + file.name;
43459                                         }
43460                                         controller.add()(o, controller.list);
43461                                     }
43462                                 });
43463                             }
43464                             ;
43465                         });
43466                     }
43467                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43468                     return DirectoryController;
43469                 })();
43470                 directives.DirectoryController = DirectoryController;
43471             })(directives = app.directives || (app.directives = {}));
43472         })(app || (app = {}));
43473         var app;
43474         (function (app) {
43475             var controllers;
43476             (function (controllers) {
43477                 var Execution = (function () {
43478                     function Execution(MyModal, $scope) {
43479                         this.MyModal = MyModal;
43480                         this.$scope = $scope;
43481                         this.commandInfoList = [];
43482                     }
43483                     ;
43484                     Execution.prototype.add = function () {
43485                         this.$scope.$broadcast('close');
43486                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43487                     };
43488                     Execution.prototype.open = function () {
43489                         var result = this.MyModal.open('SelectCommand');
43490                         console.log(result);
43491                     };
43492                     Execution.prototype.remove = function (index, list) {
43493                         list.splice(index, 1);
43494                     };
43495                     Execution.prototype.close = function () {
43496                         console.log("close");
43497                     };
43498                     Execution.$inject = ['MyModal', '$scope'];
43499                     return Execution;
43500                 })();
43501                 controllers.Execution = Execution;
43502             })(controllers = app.controllers || (app.controllers = {}));
43503         })(app || (app = {}));
43504         var app;
43505         (function (app) {
43506             var controllers;
43507             (function (controllers) {
43508                 var Workspace = (function () {
43509                     function Workspace($scope, APIEndPoint) {
43510                         this.$scope = $scope;
43511                         this.APIEndPoint = APIEndPoint;
43512                         this.directoryList = [];
43513                         var controller = this;
43514                         var directoryList = this.directoryList;
43515                         var o = {
43516                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43517                             name: '',
43518                             parentId: '',
43519                             fileType: '',
43520                             createdAt: '',
43521                             updatedAt: '',
43522                             path: '/'
43523                         };
43524                         directoryList.push(o);
43525                     }
43526                     Workspace.prototype.addDirectory = function (info, directoryList) {
43527                         directoryList.push(info);
43528                     };
43529                     Workspace.$inject = ['$scope', 'APIEndPoint'];
43530                     return Workspace;
43531                 })();
43532                 controllers.Workspace = Workspace;
43533             })(controllers = app.controllers || (app.controllers = {}));
43534         })(app || (app = {}));
43535         var app;
43536         (function (app) {
43537             var controllers;
43538             (function (controllers) {
43539                 var History = (function () {
43540                     function History($scope) {
43541                         this.page = "History";
43542                     }
43543                     History.$inject = ['$scope'];
43544                     return History;
43545                 })();
43546                 controllers.History = History;
43547             })(controllers = app.controllers || (app.controllers = {}));
43548         })(app || (app = {}));
43549         var app;
43550         (function (app) {
43551             'use strict';
43552             var appName = 'zephyr';
43553             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43554             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43555                 $urlRouterProvider.otherwise('/execution');
43556                 $locationProvider.html5Mode({
43557                     enabled: true,
43558                     requireBase: false
43559                 });
43560                 $stateProvider
43561                     .state('execution', {
43562                     url: '/execution',
43563                     templateUrl: 'templates/execution.html',
43564                     controller: 'executionController',
43565                     controllerAs: 'c'
43566                 })
43567                     .state('workspace', {
43568                     url: '/workspace',
43569                     templateUrl: 'templates/workspace.html',
43570                     controller: 'workspaceController',
43571                     controllerAs: 'c'
43572                 })
43573                     .state('history', {
43574                     url: '/history',
43575                     templateUrl: 'templates/history.html',
43576                     controller: 'historyController',
43577                     controllerAs: 'c'
43578                 });
43579             });
43580             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43581             app.zephyr.service('MyModal', app.services.MyModal);
43582             app.zephyr.controller('executionController', app.controllers.Execution);
43583             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43584             app.zephyr.controller('historyController', app.controllers.History);
43585             app.zephyr.controller('commandController', app.directives.CommandController);
43586             app.zephyr.controller('optionController', app.directives.OptionController);
43587             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43588             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43589             app.zephyr.directive('command', app.directives.Command.Factory());
43590             app.zephyr.directive('option', app.directives.Option.Factory());
43591             app.zephyr.directive('directory', app.directives.Directory.Factory());
43592         })(app || (app = {}));
43593
43594
43595 /***/ },
43596 /* 14 */
43597 /***/ function(module, exports) {
43598
43599         var app;
43600         (function (app) {
43601             var declares;
43602             (function (declares) {
43603                 var CommandInfo = (function () {
43604                     function CommandInfo(name) {
43605                         this.name = name;
43606                     }
43607                     return CommandInfo;
43608                 })();
43609                 declares.CommandInfo = CommandInfo;
43610             })(declares = app.declares || (app.declares = {}));
43611         })(app || (app = {}));
43612         var app;
43613         (function (app) {
43614             var services;
43615             (function (services) {
43616                 var APIEndPoint = (function () {
43617                     function APIEndPoint($resource) {
43618                         this.$resource = $resource;
43619                     }
43620                     APIEndPoint.prototype.resource = function (endPoint) {
43621                         var customAction = {
43622                             method: 'GET',
43623                             isArray: false
43624                         };
43625                         return this.$resource(endPoint, {}, { customAction: customAction });
43626                     };
43627                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43628                         var endPoint = '/api/optionControlFile/' + command;
43629                         return this.resource(endPoint).get();
43630                     };
43631                     APIEndPoint.prototype.getFiles = function (fileId) {
43632                         var endPoint = '/api/v1/workspace';
43633                         if (fileId) {
43634                             endPoint += '/' + fileId;
43635                         }
43636                         return this.resource(endPoint).get();
43637                     };
43638                     APIEndPoint.prototype.getDirectories = function () {
43639                         var endPoint = '/api/v1/all/workspace/directory';
43640                         return this.resource(endPoint).get();
43641                     };
43642                     return APIEndPoint;
43643                 })();
43644                 services.APIEndPoint = APIEndPoint;
43645             })(services = app.services || (app.services = {}));
43646         })(app || (app = {}));
43647         var app;
43648         (function (app) {
43649             var services;
43650             (function (services) {
43651                 var MyModal = (function () {
43652                     function MyModal($uibModal) {
43653                         this.$uibModal = $uibModal;
43654                         this.modalOption = {
43655                             backdrop: true,
43656                             controller: null,
43657                             templateUrl: null,
43658                             size: null
43659                         };
43660                     }
43661                     MyModal.prototype.open = function (modalName) {
43662                         if (modalName === 'SelectCommand') {
43663                             this.modalOption.templateUrl = 'templates/select-command.html';
43664                             this.modalOption.size = 'lg';
43665                         }
43666                         return this.$uibModal.open(this.modalOption);
43667                     };
43668                     return MyModal;
43669                 })();
43670                 services.MyModal = MyModal;
43671             })(services = app.services || (app.services = {}));
43672         })(app || (app = {}));
43673         var app;
43674         (function (app) {
43675             var directives;
43676             (function (directives) {
43677                 var Command = (function () {
43678                     function Command() {
43679                         this.restrict = 'E';
43680                         this.replace = true;
43681                         this.scope = true;
43682                         this.controller = 'commandController';
43683                         this.controllerAs = 'ctrl';
43684                         this.bindToController = {
43685                             index: '=',
43686                             name: '=',
43687                             remove: '&',
43688                             list: '='
43689                         };
43690                         this.templateUrl = 'templates/command.html';
43691                     }
43692                     Command.Factory = function () {
43693                         var directive = function () {
43694                             return new Command();
43695                         };
43696                         directive.$inject = [];
43697                         return directive;
43698                     };
43699                     return Command;
43700                 })();
43701                 directives.Command = Command;
43702                 var CommandController = (function () {
43703                     function CommandController(APIEndPoint, $scope) {
43704                         this.APIEndPoint = APIEndPoint;
43705                         this.$scope = $scope;
43706                         var controller = this;
43707                         this.APIEndPoint
43708                             .getOptionControlFile('dcdFilePrint')
43709                             .$promise
43710                             .then(function (result) {
43711                             controller.options = result.info;
43712                         });
43713                         this.APIEndPoint
43714                             .getDirectories()
43715                             .$promise
43716                             .then(function (result) {
43717                             controller.dirs = result.info;
43718                         });
43719                         this.heading = "[" + this.index + "]: dcdFilePring";
43720                         this.isOpen = true;
43721                         this.$scope.$on('close', function () {
43722                             controller.isOpen = false;
43723                         });
43724                     }
43725                     CommandController.prototype.submit = function () {
43726                         var opt = [];
43727                         angular.forEach(this.options, function (option) {
43728                             var obj = {
43729                                 name: option.option,
43730                                 arguments: []
43731                             };
43732                             angular.forEach(option.arg, function (arg) {
43733                                 if (arg.input) {
43734                                     obj.arguments.push(arg.input);
43735                                 }
43736                             });
43737                             if (obj.arguments.length > 0) {
43738                                 opt.push(obj);
43739                             }
43740                         });
43741                         var execObj = {
43742                             command: this.name,
43743                             workspace: this.workspace.fileId,
43744                             option: opt
43745                         };
43746                         console.log(JSON.stringify(execObj, null, '\t'));
43747                     };
43748                     CommandController.prototype.removeMySelf = function (index) {
43749                         this.remove()(index, this.list);
43750                     };
43751                     CommandController.prototype.reloadFiles = function () {
43752                         var _this = this;
43753                         var fileId = this.workspace.fileId;
43754                         this.APIEndPoint
43755                             .getFiles(fileId)
43756                             .$promise
43757                             .then(function (result) {
43758                             var status = result.status;
43759                             if (status === 'success') {
43760                                 _this.files = result.info;
43761                             }
43762                             else {
43763                                 console.log(result.message);
43764                             }
43765                         });
43766                     };
43767                     CommandController.prototype.debug = function () {
43768                         console.log(this.files);
43769                         console.log(this.files);
43770                         console.log(this.workspace);
43771                     };
43772                     CommandController.$inject = ['APIEndPoint', '$scope'];
43773                     return CommandController;
43774                 })();
43775                 directives.CommandController = CommandController;
43776             })(directives = app.directives || (app.directives = {}));
43777         })(app || (app = {}));
43778         var app;
43779         (function (app) {
43780             var directives;
43781             (function (directives) {
43782                 var HeaderMenu = (function () {
43783                     function HeaderMenu() {
43784                         this.restrict = 'E';
43785                         this.replace = true;
43786                         this.templateUrl = 'templates/header-menu.html';
43787                     }
43788                     HeaderMenu.Factory = function () {
43789                         var directive = function () {
43790                             return new HeaderMenu();
43791                         };
43792                         return directive;
43793                     };
43794                     return HeaderMenu;
43795                 })();
43796                 directives.HeaderMenu = HeaderMenu;
43797             })(directives = app.directives || (app.directives = {}));
43798         })(app || (app = {}));
43799         var app;
43800         (function (app) {
43801             var directives;
43802             (function (directives) {
43803                 var Option = (function () {
43804                     function Option() {
43805                         this.restrict = 'E';
43806                         this.replace = true;
43807                         this.controller = 'optionController';
43808                         this.bindToController = {
43809                             info: '=',
43810                             files: '='
43811                         };
43812                         this.scope = true;
43813                         this.templateUrl = 'templates/option.html';
43814                         this.controllerAs = 'ctrl';
43815                     }
43816                     Option.Factory = function () {
43817                         var directive = function () {
43818                             return new Option();
43819                         };
43820                         directive.$inject = [];
43821                         return directive;
43822                     };
43823                     return Option;
43824                 })();
43825                 directives.Option = Option;
43826                 var OptionController = (function () {
43827                     function OptionController() {
43828                         var controller = this;
43829                         angular.forEach(controller.info.arg, function (arg) {
43830                             if (arg.initialValue) {
43831                                 if (arg.formType === 'number') {
43832                                     arg.input = parseInt(arg.initialValue);
43833                                 }
43834                                 else {
43835                                     arg.input = arg.initialValue;
43836                                 }
43837                             }
43838                         });
43839                     }
43840                     OptionController.$inject = [];
43841                     return OptionController;
43842                 })();
43843                 directives.OptionController = OptionController;
43844             })(directives = app.directives || (app.directives = {}));
43845         })(app || (app = {}));
43846         var app;
43847         (function (app) {
43848             var directives;
43849             (function (directives) {
43850                 var Directory = (function () {
43851                     function Directory() {
43852                         this.restrict = 'E';
43853                         this.replace = true;
43854                         this.controller = 'directoryController';
43855                         this.controllerAs = 'ctrl';
43856                         this.bindToController = {
43857                             info: '=',
43858                             add: '&',
43859                             list: '=',
43860                             files: '='
43861                         };
43862                         this.templateUrl = 'templates/directory.html';
43863                     }
43864                     Directory.Factory = function () {
43865                         var directive = function () {
43866                             return new Directory();
43867                         };
43868                         return directive;
43869                     };
43870                     return Directory;
43871                 })();
43872                 directives.Directory = Directory;
43873                 var DirectoryController = (function () {
43874                     function DirectoryController(APIEndPoint, $scope) {
43875                         this.APIEndPoint = APIEndPoint;
43876                         this.$scope = $scope;
43877                         var controller = this;
43878                         this.APIEndPoint
43879                             .getFiles(this.info.fileId)
43880                             .$promise
43881                             .then(function (result) {
43882                             if (result.status === 'success') {
43883                                 controller.files = result.info;
43884                                 angular.forEach(result.info, function (file) {
43885                                     if (file.fileType === '0') {
43886                                         var o = file;
43887                                         if (controller.info.path === '/') {
43888                                             o.path = '/' + file.name;
43889                                         }
43890                                         else {
43891                                             o.path = controller.info.path + '/' + file.name;
43892                                         }
43893                                         controller.add()(o, controller.list);
43894                                     }
43895                                 });
43896                             }
43897                             ;
43898                         });
43899                     }
43900                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43901                     return DirectoryController;
43902                 })();
43903                 directives.DirectoryController = DirectoryController;
43904             })(directives = app.directives || (app.directives = {}));
43905         })(app || (app = {}));
43906         var app;
43907         (function (app) {
43908             var controllers;
43909             (function (controllers) {
43910                 var Execution = (function () {
43911                     function Execution(MyModal, $scope) {
43912                         this.MyModal = MyModal;
43913                         this.$scope = $scope;
43914                         this.commandInfoList = [];
43915                     }
43916                     ;
43917                     Execution.prototype.add = function () {
43918                         this.$scope.$broadcast('close');
43919                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43920                     };
43921                     Execution.prototype.open = function () {
43922                         var result = this.MyModal.open('SelectCommand');
43923                         console.log(result);
43924                     };
43925                     Execution.prototype.remove = function (index, list) {
43926                         list.splice(index, 1);
43927                     };
43928                     Execution.prototype.close = function () {
43929                         console.log("close");
43930                     };
43931                     Execution.$inject = ['MyModal', '$scope'];
43932                     return Execution;
43933                 })();
43934                 controllers.Execution = Execution;
43935             })(controllers = app.controllers || (app.controllers = {}));
43936         })(app || (app = {}));
43937         var app;
43938         (function (app) {
43939             var controllers;
43940             (function (controllers) {
43941                 var Workspace = (function () {
43942                     function Workspace($scope, APIEndPoint) {
43943                         this.$scope = $scope;
43944                         this.APIEndPoint = APIEndPoint;
43945                         this.directoryList = [];
43946                         var controller = this;
43947                         var directoryList = this.directoryList;
43948                         var o = {
43949                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43950                             name: '',
43951                             parentId: '',
43952                             fileType: '',
43953                             createdAt: '',
43954                             updatedAt: '',
43955                             path: '/'
43956                         };
43957                         directoryList.push(o);
43958                     }
43959                     Workspace.prototype.addDirectory = function (info, directoryList) {
43960                         directoryList.push(info);
43961                     };
43962                     Workspace.$inject = ['$scope', 'APIEndPoint'];
43963                     return Workspace;
43964                 })();
43965                 controllers.Workspace = Workspace;
43966             })(controllers = app.controllers || (app.controllers = {}));
43967         })(app || (app = {}));
43968         var app;
43969         (function (app) {
43970             var controllers;
43971             (function (controllers) {
43972                 var History = (function () {
43973                     function History($scope) {
43974                         this.page = "History";
43975                     }
43976                     History.$inject = ['$scope'];
43977                     return History;
43978                 })();
43979                 controllers.History = History;
43980             })(controllers = app.controllers || (app.controllers = {}));
43981         })(app || (app = {}));
43982         var app;
43983         (function (app) {
43984             'use strict';
43985             var appName = 'zephyr';
43986             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43987             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43988                 $urlRouterProvider.otherwise('/execution');
43989                 $locationProvider.html5Mode({
43990                     enabled: true,
43991                     requireBase: false
43992                 });
43993                 $stateProvider
43994                     .state('execution', {
43995                     url: '/execution',
43996                     templateUrl: 'templates/execution.html',
43997                     controller: 'executionController',
43998                     controllerAs: 'c'
43999                 })
44000                     .state('workspace', {
44001                     url: '/workspace',
44002                     templateUrl: 'templates/workspace.html',
44003                     controller: 'workspaceController',
44004                     controllerAs: 'c'
44005                 })
44006                     .state('history', {
44007                     url: '/history',
44008                     templateUrl: 'templates/history.html',
44009                     controller: 'historyController',
44010                     controllerAs: 'c'
44011                 });
44012             });
44013             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44014             app.zephyr.service('MyModal', app.services.MyModal);
44015             app.zephyr.controller('executionController', app.controllers.Execution);
44016             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44017             app.zephyr.controller('historyController', app.controllers.History);
44018             app.zephyr.controller('commandController', app.directives.CommandController);
44019             app.zephyr.controller('optionController', app.directives.OptionController);
44020             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44021             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44022             app.zephyr.directive('command', app.directives.Command.Factory());
44023             app.zephyr.directive('option', app.directives.Option.Factory());
44024             app.zephyr.directive('directory', app.directives.Directory.Factory());
44025         })(app || (app = {}));
44026
44027
44028 /***/ },
44029 /* 15 */
44030 /***/ function(module, exports) {
44031
44032         var app;
44033         (function (app) {
44034             var declares;
44035             (function (declares) {
44036                 var CommandInfo = (function () {
44037                     function CommandInfo(name) {
44038                         this.name = name;
44039                     }
44040                     return CommandInfo;
44041                 })();
44042                 declares.CommandInfo = CommandInfo;
44043             })(declares = app.declares || (app.declares = {}));
44044         })(app || (app = {}));
44045         var app;
44046         (function (app) {
44047             var services;
44048             (function (services) {
44049                 var APIEndPoint = (function () {
44050                     function APIEndPoint($resource) {
44051                         this.$resource = $resource;
44052                     }
44053                     APIEndPoint.prototype.resource = function (endPoint) {
44054                         var customAction = {
44055                             method: 'GET',
44056                             isArray: false
44057                         };
44058                         return this.$resource(endPoint, {}, { customAction: customAction });
44059                     };
44060                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44061                         var endPoint = '/api/optionControlFile/' + command;
44062                         return this.resource(endPoint).get();
44063                     };
44064                     APIEndPoint.prototype.getFiles = function (fileId) {
44065                         var endPoint = '/api/v1/workspace';
44066                         if (fileId) {
44067                             endPoint += '/' + fileId;
44068                         }
44069                         return this.resource(endPoint).get();
44070                     };
44071                     APIEndPoint.prototype.getDirectories = function () {
44072                         var endPoint = '/api/v1/all/workspace/directory';
44073                         return this.resource(endPoint).get();
44074                     };
44075                     return APIEndPoint;
44076                 })();
44077                 services.APIEndPoint = APIEndPoint;
44078             })(services = app.services || (app.services = {}));
44079         })(app || (app = {}));
44080         var app;
44081         (function (app) {
44082             var services;
44083             (function (services) {
44084                 var MyModal = (function () {
44085                     function MyModal($uibModal) {
44086                         this.$uibModal = $uibModal;
44087                         this.modalOption = {
44088                             backdrop: true,
44089                             controller: null,
44090                             templateUrl: null,
44091                             size: null
44092                         };
44093                     }
44094                     MyModal.prototype.open = function (modalName) {
44095                         if (modalName === 'SelectCommand') {
44096                             this.modalOption.templateUrl = 'templates/select-command.html';
44097                             this.modalOption.size = 'lg';
44098                         }
44099                         return this.$uibModal.open(this.modalOption);
44100                     };
44101                     return MyModal;
44102                 })();
44103                 services.MyModal = MyModal;
44104             })(services = app.services || (app.services = {}));
44105         })(app || (app = {}));
44106         var app;
44107         (function (app) {
44108             var directives;
44109             (function (directives) {
44110                 var Command = (function () {
44111                     function Command() {
44112                         this.restrict = 'E';
44113                         this.replace = true;
44114                         this.scope = true;
44115                         this.controller = 'commandController';
44116                         this.controllerAs = 'ctrl';
44117                         this.bindToController = {
44118                             index: '=',
44119                             name: '=',
44120                             remove: '&',
44121                             list: '='
44122                         };
44123                         this.templateUrl = 'templates/command.html';
44124                     }
44125                     Command.Factory = function () {
44126                         var directive = function () {
44127                             return new Command();
44128                         };
44129                         directive.$inject = [];
44130                         return directive;
44131                     };
44132                     return Command;
44133                 })();
44134                 directives.Command = Command;
44135                 var CommandController = (function () {
44136                     function CommandController(APIEndPoint, $scope) {
44137                         this.APIEndPoint = APIEndPoint;
44138                         this.$scope = $scope;
44139                         var controller = this;
44140                         this.APIEndPoint
44141                             .getOptionControlFile('dcdFilePrint')
44142                             .$promise
44143                             .then(function (result) {
44144                             controller.options = result.info;
44145                         });
44146                         this.APIEndPoint
44147                             .getDirectories()
44148                             .$promise
44149                             .then(function (result) {
44150                             controller.dirs = result.info;
44151                         });
44152                         this.heading = "[" + this.index + "]: dcdFilePring";
44153                         this.isOpen = true;
44154                         this.$scope.$on('close', function () {
44155                             controller.isOpen = false;
44156                         });
44157                     }
44158                     CommandController.prototype.submit = function () {
44159                         var opt = [];
44160                         angular.forEach(this.options, function (option) {
44161                             var obj = {
44162                                 name: option.option,
44163                                 arguments: []
44164                             };
44165                             angular.forEach(option.arg, function (arg) {
44166                                 if (arg.input) {
44167                                     obj.arguments.push(arg.input);
44168                                 }
44169                             });
44170                             if (obj.arguments.length > 0) {
44171                                 opt.push(obj);
44172                             }
44173                         });
44174                         var execObj = {
44175                             command: this.name,
44176                             workspace: this.workspace.fileId,
44177                             option: opt
44178                         };
44179                         console.log(JSON.stringify(execObj, null, '\t'));
44180                     };
44181                     CommandController.prototype.removeMySelf = function (index) {
44182                         this.remove()(index, this.list);
44183                     };
44184                     CommandController.prototype.reloadFiles = function () {
44185                         var _this = this;
44186                         var fileId = this.workspace.fileId;
44187                         this.APIEndPoint
44188                             .getFiles(fileId)
44189                             .$promise
44190                             .then(function (result) {
44191                             var status = result.status;
44192                             if (status === 'success') {
44193                                 _this.files = result.info;
44194                             }
44195                             else {
44196                                 console.log(result.message);
44197                             }
44198                         });
44199                     };
44200                     CommandController.prototype.debug = function () {
44201                         console.log(this.files);
44202                         console.log(this.files);
44203                         console.log(this.workspace);
44204                     };
44205                     CommandController.$inject = ['APIEndPoint', '$scope'];
44206                     return CommandController;
44207                 })();
44208                 directives.CommandController = CommandController;
44209             })(directives = app.directives || (app.directives = {}));
44210         })(app || (app = {}));
44211         var app;
44212         (function (app) {
44213             var directives;
44214             (function (directives) {
44215                 var HeaderMenu = (function () {
44216                     function HeaderMenu() {
44217                         this.restrict = 'E';
44218                         this.replace = true;
44219                         this.templateUrl = 'templates/header-menu.html';
44220                     }
44221                     HeaderMenu.Factory = function () {
44222                         var directive = function () {
44223                             return new HeaderMenu();
44224                         };
44225                         return directive;
44226                     };
44227                     return HeaderMenu;
44228                 })();
44229                 directives.HeaderMenu = HeaderMenu;
44230             })(directives = app.directives || (app.directives = {}));
44231         })(app || (app = {}));
44232         var app;
44233         (function (app) {
44234             var directives;
44235             (function (directives) {
44236                 var Option = (function () {
44237                     function Option() {
44238                         this.restrict = 'E';
44239                         this.replace = true;
44240                         this.controller = 'optionController';
44241                         this.bindToController = {
44242                             info: '=',
44243                             files: '='
44244                         };
44245                         this.scope = true;
44246                         this.templateUrl = 'templates/option.html';
44247                         this.controllerAs = 'ctrl';
44248                     }
44249                     Option.Factory = function () {
44250                         var directive = function () {
44251                             return new Option();
44252                         };
44253                         directive.$inject = [];
44254                         return directive;
44255                     };
44256                     return Option;
44257                 })();
44258                 directives.Option = Option;
44259                 var OptionController = (function () {
44260                     function OptionController() {
44261                         var controller = this;
44262                         angular.forEach(controller.info.arg, function (arg) {
44263                             if (arg.initialValue) {
44264                                 if (arg.formType === 'number') {
44265                                     arg.input = parseInt(arg.initialValue);
44266                                 }
44267                                 else {
44268                                     arg.input = arg.initialValue;
44269                                 }
44270                             }
44271                         });
44272                     }
44273                     OptionController.$inject = [];
44274                     return OptionController;
44275                 })();
44276                 directives.OptionController = OptionController;
44277             })(directives = app.directives || (app.directives = {}));
44278         })(app || (app = {}));
44279         var app;
44280         (function (app) {
44281             var directives;
44282             (function (directives) {
44283                 var Directory = (function () {
44284                     function Directory() {
44285                         this.restrict = 'E';
44286                         this.replace = true;
44287                         this.controller = 'directoryController';
44288                         this.controllerAs = 'ctrl';
44289                         this.bindToController = {
44290                             info: '=',
44291                             add: '&',
44292                             list: '=',
44293                             files: '='
44294                         };
44295                         this.templateUrl = 'templates/directory.html';
44296                     }
44297                     Directory.Factory = function () {
44298                         var directive = function () {
44299                             return new Directory();
44300                         };
44301                         return directive;
44302                     };
44303                     return Directory;
44304                 })();
44305                 directives.Directory = Directory;
44306                 var DirectoryController = (function () {
44307                     function DirectoryController(APIEndPoint, $scope) {
44308                         this.APIEndPoint = APIEndPoint;
44309                         this.$scope = $scope;
44310                         var controller = this;
44311                         this.APIEndPoint
44312                             .getFiles(this.info.fileId)
44313                             .$promise
44314                             .then(function (result) {
44315                             if (result.status === 'success') {
44316                                 controller.files = result.info;
44317                                 angular.forEach(result.info, function (file) {
44318                                     if (file.fileType === '0') {
44319                                         var o = file;
44320                                         if (controller.info.path === '/') {
44321                                             o.path = '/' + file.name;
44322                                         }
44323                                         else {
44324                                             o.path = controller.info.path + '/' + file.name;
44325                                         }
44326                                         controller.add()(o, controller.list);
44327                                     }
44328                                 });
44329                             }
44330                             ;
44331                         });
44332                     }
44333                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44334                     return DirectoryController;
44335                 })();
44336                 directives.DirectoryController = DirectoryController;
44337             })(directives = app.directives || (app.directives = {}));
44338         })(app || (app = {}));
44339         var app;
44340         (function (app) {
44341             var controllers;
44342             (function (controllers) {
44343                 var Execution = (function () {
44344                     function Execution(MyModal, $scope) {
44345                         this.MyModal = MyModal;
44346                         this.$scope = $scope;
44347                         this.commandInfoList = [];
44348                     }
44349                     ;
44350                     Execution.prototype.add = function () {
44351                         this.$scope.$broadcast('close');
44352                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
44353                     };
44354                     Execution.prototype.open = function () {
44355                         var result = this.MyModal.open('SelectCommand');
44356                         console.log(result);
44357                     };
44358                     Execution.prototype.remove = function (index, list) {
44359                         list.splice(index, 1);
44360                     };
44361                     Execution.prototype.close = function () {
44362                         console.log("close");
44363                     };
44364                     Execution.$inject = ['MyModal', '$scope'];
44365                     return Execution;
44366                 })();
44367                 controllers.Execution = Execution;
44368             })(controllers = app.controllers || (app.controllers = {}));
44369         })(app || (app = {}));
44370         var app;
44371         (function (app) {
44372             var controllers;
44373             (function (controllers) {
44374                 var Workspace = (function () {
44375                     function Workspace($scope, APIEndPoint) {
44376                         this.$scope = $scope;
44377                         this.APIEndPoint = APIEndPoint;
44378                         this.directoryList = [];
44379                         var controller = this;
44380                         var directoryList = this.directoryList;
44381                         var o = {
44382                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44383                             name: '',
44384                             parentId: '',
44385                             fileType: '',
44386                             createdAt: '',
44387                             updatedAt: '',
44388                             path: '/'
44389                         };
44390                         directoryList.push(o);
44391                     }
44392                     Workspace.prototype.addDirectory = function (info, directoryList) {
44393                         directoryList.push(info);
44394                     };
44395                     Workspace.$inject = ['$scope', 'APIEndPoint'];
44396                     return Workspace;
44397                 })();
44398                 controllers.Workspace = Workspace;
44399             })(controllers = app.controllers || (app.controllers = {}));
44400         })(app || (app = {}));
44401         var app;
44402         (function (app) {
44403             var controllers;
44404             (function (controllers) {
44405                 var History = (function () {
44406                     function History($scope) {
44407                         this.page = "History";
44408                     }
44409                     History.$inject = ['$scope'];
44410                     return History;
44411                 })();
44412                 controllers.History = History;
44413             })(controllers = app.controllers || (app.controllers = {}));
44414         })(app || (app = {}));
44415         var app;
44416         (function (app) {
44417             'use strict';
44418             var appName = 'zephyr';
44419             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44420             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44421                 $urlRouterProvider.otherwise('/execution');
44422                 $locationProvider.html5Mode({
44423                     enabled: true,
44424                     requireBase: false
44425                 });
44426                 $stateProvider
44427                     .state('execution', {
44428                     url: '/execution',
44429                     templateUrl: 'templates/execution.html',
44430                     controller: 'executionController',
44431                     controllerAs: 'c'
44432                 })
44433                     .state('workspace', {
44434                     url: '/workspace',
44435                     templateUrl: 'templates/workspace.html',
44436                     controller: 'workspaceController',
44437                     controllerAs: 'c'
44438                 })
44439                     .state('history', {
44440                     url: '/history',
44441                     templateUrl: 'templates/history.html',
44442                     controller: 'historyController',
44443                     controllerAs: 'c'
44444                 });
44445             });
44446             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44447             app.zephyr.service('MyModal', app.services.MyModal);
44448             app.zephyr.controller('executionController', app.controllers.Execution);
44449             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44450             app.zephyr.controller('historyController', app.controllers.History);
44451             app.zephyr.controller('commandController', app.directives.CommandController);
44452             app.zephyr.controller('optionController', app.directives.OptionController);
44453             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44454             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44455             app.zephyr.directive('command', app.directives.Command.Factory());
44456             app.zephyr.directive('option', app.directives.Option.Factory());
44457             app.zephyr.directive('directory', app.directives.Directory.Factory());
44458         })(app || (app = {}));
44459
44460
44461 /***/ },
44462 /* 16 */
44463 /***/ function(module, exports) {
44464
44465         var app;
44466         (function (app) {
44467             var declares;
44468             (function (declares) {
44469                 var CommandInfo = (function () {
44470                     function CommandInfo(name) {
44471                         this.name = name;
44472                     }
44473                     return CommandInfo;
44474                 })();
44475                 declares.CommandInfo = CommandInfo;
44476             })(declares = app.declares || (app.declares = {}));
44477         })(app || (app = {}));
44478         var app;
44479         (function (app) {
44480             var services;
44481             (function (services) {
44482                 var APIEndPoint = (function () {
44483                     function APIEndPoint($resource) {
44484                         this.$resource = $resource;
44485                     }
44486                     APIEndPoint.prototype.resource = function (endPoint) {
44487                         var customAction = {
44488                             method: 'GET',
44489                             isArray: false
44490                         };
44491                         return this.$resource(endPoint, {}, { customAction: customAction });
44492                     };
44493                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44494                         var endPoint = '/api/optionControlFile/' + command;
44495                         return this.resource(endPoint).get();
44496                     };
44497                     APIEndPoint.prototype.getFiles = function (fileId) {
44498                         var endPoint = '/api/v1/workspace';
44499                         if (fileId) {
44500                             endPoint += '/' + fileId;
44501                         }
44502                         return this.resource(endPoint).get();
44503                     };
44504                     APIEndPoint.prototype.getDirectories = function () {
44505                         var endPoint = '/api/v1/all/workspace/directory';
44506                         return this.resource(endPoint).get();
44507                     };
44508                     return APIEndPoint;
44509                 })();
44510                 services.APIEndPoint = APIEndPoint;
44511             })(services = app.services || (app.services = {}));
44512         })(app || (app = {}));
44513         var app;
44514         (function (app) {
44515             var services;
44516             (function (services) {
44517                 var MyModal = (function () {
44518                     function MyModal($uibModal) {
44519                         this.$uibModal = $uibModal;
44520                         this.modalOption = {
44521                             backdrop: true,
44522                             controller: null,
44523                             templateUrl: null,
44524                             size: null
44525                         };
44526                     }
44527                     MyModal.prototype.open = function (modalName) {
44528                         if (modalName === 'SelectCommand') {
44529                             this.modalOption.templateUrl = 'templates/select-command.html';
44530                             this.modalOption.size = 'lg';
44531                         }
44532                         return this.$uibModal.open(this.modalOption);
44533                     };
44534                     return MyModal;
44535                 })();
44536                 services.MyModal = MyModal;
44537             })(services = app.services || (app.services = {}));
44538         })(app || (app = {}));
44539         var app;
44540         (function (app) {
44541             var directives;
44542             (function (directives) {
44543                 var Command = (function () {
44544                     function Command() {
44545                         this.restrict = 'E';
44546                         this.replace = true;
44547                         this.scope = true;
44548                         this.controller = 'commandController';
44549                         this.controllerAs = 'ctrl';
44550                         this.bindToController = {
44551                             index: '=',
44552                             name: '=',
44553                             remove: '&',
44554                             list: '='
44555                         };
44556                         this.templateUrl = 'templates/command.html';
44557                     }
44558                     Command.Factory = function () {
44559                         var directive = function () {
44560                             return new Command();
44561                         };
44562                         directive.$inject = [];
44563                         return directive;
44564                     };
44565                     return Command;
44566                 })();
44567                 directives.Command = Command;
44568                 var CommandController = (function () {
44569                     function CommandController(APIEndPoint, $scope) {
44570                         this.APIEndPoint = APIEndPoint;
44571                         this.$scope = $scope;
44572                         var controller = this;
44573                         this.APIEndPoint
44574                             .getOptionControlFile('dcdFilePrint')
44575                             .$promise
44576                             .then(function (result) {
44577                             controller.options = result.info;
44578                         });
44579                         this.APIEndPoint
44580                             .getDirectories()
44581                             .$promise
44582                             .then(function (result) {
44583                             controller.dirs = result.info;
44584                         });
44585                         this.heading = "[" + this.index + "]: dcdFilePring";
44586                         this.isOpen = true;
44587                         this.$scope.$on('close', function () {
44588                             controller.isOpen = false;
44589                         });
44590                     }
44591                     CommandController.prototype.submit = function () {
44592                         var opt = [];
44593                         angular.forEach(this.options, function (option) {
44594                             var obj = {
44595                                 name: option.option,
44596                                 arguments: []
44597                             };
44598                             angular.forEach(option.arg, function (arg) {
44599                                 if (arg.input) {
44600                                     obj.arguments.push(arg.input);
44601                                 }
44602                             });
44603                             if (obj.arguments.length > 0) {
44604                                 opt.push(obj);
44605                             }
44606                         });
44607                         var execObj = {
44608                             command: this.name,
44609                             workspace: this.workspace.fileId,
44610                             option: opt
44611                         };
44612                         console.log(JSON.stringify(execObj, null, '\t'));
44613                     };
44614                     CommandController.prototype.removeMySelf = function (index) {
44615                         this.remove()(index, this.list);
44616                     };
44617                     CommandController.prototype.reloadFiles = function () {
44618                         var _this = this;
44619                         var fileId = this.workspace.fileId;
44620                         this.APIEndPoint
44621                             .getFiles(fileId)
44622                             .$promise
44623                             .then(function (result) {
44624                             var status = result.status;
44625                             if (status === 'success') {
44626                                 _this.files = result.info;
44627                             }
44628                             else {
44629                                 console.log(result.message);
44630                             }
44631                         });
44632                     };
44633                     CommandController.prototype.debug = function () {
44634                         console.log(this.files);
44635                         console.log(this.files);
44636                         console.log(this.workspace);
44637                     };
44638                     CommandController.$inject = ['APIEndPoint', '$scope'];
44639                     return CommandController;
44640                 })();
44641                 directives.CommandController = CommandController;
44642             })(directives = app.directives || (app.directives = {}));
44643         })(app || (app = {}));
44644         var app;
44645         (function (app) {
44646             var directives;
44647             (function (directives) {
44648                 var HeaderMenu = (function () {
44649                     function HeaderMenu() {
44650                         this.restrict = 'E';
44651                         this.replace = true;
44652                         this.templateUrl = 'templates/header-menu.html';
44653                     }
44654                     HeaderMenu.Factory = function () {
44655                         var directive = function () {
44656                             return new HeaderMenu();
44657                         };
44658                         return directive;
44659                     };
44660                     return HeaderMenu;
44661                 })();
44662                 directives.HeaderMenu = HeaderMenu;
44663             })(directives = app.directives || (app.directives = {}));
44664         })(app || (app = {}));
44665         var app;
44666         (function (app) {
44667             var directives;
44668             (function (directives) {
44669                 var Option = (function () {
44670                     function Option() {
44671                         this.restrict = 'E';
44672                         this.replace = true;
44673                         this.controller = 'optionController';
44674                         this.bindToController = {
44675                             info: '=',
44676                             files: '='
44677                         };
44678                         this.scope = true;
44679                         this.templateUrl = 'templates/option.html';
44680                         this.controllerAs = 'ctrl';
44681                     }
44682                     Option.Factory = function () {
44683                         var directive = function () {
44684                             return new Option();
44685                         };
44686                         directive.$inject = [];
44687                         return directive;
44688                     };
44689                     return Option;
44690                 })();
44691                 directives.Option = Option;
44692                 var OptionController = (function () {
44693                     function OptionController() {
44694                         var controller = this;
44695                         angular.forEach(controller.info.arg, function (arg) {
44696                             if (arg.initialValue) {
44697                                 if (arg.formType === 'number') {
44698                                     arg.input = parseInt(arg.initialValue);
44699                                 }
44700                                 else {
44701                                     arg.input = arg.initialValue;
44702                                 }
44703                             }
44704                         });
44705                     }
44706                     OptionController.$inject = [];
44707                     return OptionController;
44708                 })();
44709                 directives.OptionController = OptionController;
44710             })(directives = app.directives || (app.directives = {}));
44711         })(app || (app = {}));
44712         var app;
44713         (function (app) {
44714             var directives;
44715             (function (directives) {
44716                 var Directory = (function () {
44717                     function Directory() {
44718                         this.restrict = 'E';
44719                         this.replace = true;
44720                         this.controller = 'directoryController';
44721                         this.controllerAs = 'ctrl';
44722                         this.bindToController = {
44723                             info: '=',
44724                             add: '&',
44725                             list: '=',
44726                             files: '='
44727                         };
44728                         this.templateUrl = 'templates/directory.html';
44729                     }
44730                     Directory.Factory = function () {
44731                         var directive = function () {
44732                             return new Directory();
44733                         };
44734                         return directive;
44735                     };
44736                     return Directory;
44737                 })();
44738                 directives.Directory = Directory;
44739                 var DirectoryController = (function () {
44740                     function DirectoryController(APIEndPoint, $scope) {
44741                         this.APIEndPoint = APIEndPoint;
44742                         this.$scope = $scope;
44743                         var controller = this;
44744                         this.APIEndPoint
44745                             .getFiles(this.info.fileId)
44746                             .$promise
44747                             .then(function (result) {
44748                             if (result.status === 'success') {
44749                                 controller.files = result.info;
44750                                 angular.forEach(result.info, function (file) {
44751                                     if (file.fileType === '0') {
44752                                         var o = file;
44753                                         if (controller.info.path === '/') {
44754                                             o.path = '/' + file.name;
44755                                         }
44756                                         else {
44757                                             o.path = controller.info.path + '/' + file.name;
44758                                         }
44759                                         controller.add()(o, controller.list);
44760                                     }
44761                                 });
44762                             }
44763                             ;
44764                         });
44765                     }
44766                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44767                     return DirectoryController;
44768                 })();
44769                 directives.DirectoryController = DirectoryController;
44770             })(directives = app.directives || (app.directives = {}));
44771         })(app || (app = {}));
44772         var app;
44773         (function (app) {
44774             var controllers;
44775             (function (controllers) {
44776                 var Execution = (function () {
44777                     function Execution(MyModal, $scope) {
44778                         this.MyModal = MyModal;
44779                         this.$scope = $scope;
44780                         this.commandInfoList = [];
44781                     }
44782                     ;
44783                     Execution.prototype.add = function () {
44784                         this.$scope.$broadcast('close');
44785                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
44786                     };
44787                     Execution.prototype.open = function () {
44788                         var result = this.MyModal.open('SelectCommand');
44789                         console.log(result);
44790                     };
44791                     Execution.prototype.remove = function (index, list) {
44792                         list.splice(index, 1);
44793                     };
44794                     Execution.prototype.close = function () {
44795                         console.log("close");
44796                     };
44797                     Execution.$inject = ['MyModal', '$scope'];
44798                     return Execution;
44799                 })();
44800                 controllers.Execution = Execution;
44801             })(controllers = app.controllers || (app.controllers = {}));
44802         })(app || (app = {}));
44803         var app;
44804         (function (app) {
44805             var controllers;
44806             (function (controllers) {
44807                 var Workspace = (function () {
44808                     function Workspace($scope, APIEndPoint) {
44809                         this.$scope = $scope;
44810                         this.APIEndPoint = APIEndPoint;
44811                         this.directoryList = [];
44812                         var controller = this;
44813                         var directoryList = this.directoryList;
44814                         var o = {
44815                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44816                             name: '',
44817                             parentId: '',
44818                             fileType: '',
44819                             createdAt: '',
44820                             updatedAt: '',
44821                             path: '/'
44822                         };
44823                         directoryList.push(o);
44824                     }
44825                     Workspace.prototype.addDirectory = function (info, directoryList) {
44826                         directoryList.push(info);
44827                     };
44828                     Workspace.$inject = ['$scope', 'APIEndPoint'];
44829                     return Workspace;
44830                 })();
44831                 controllers.Workspace = Workspace;
44832             })(controllers = app.controllers || (app.controllers = {}));
44833         })(app || (app = {}));
44834         var app;
44835         (function (app) {
44836             var controllers;
44837             (function (controllers) {
44838                 var History = (function () {
44839                     function History($scope) {
44840                         this.page = "History";
44841                     }
44842                     History.$inject = ['$scope'];
44843                     return History;
44844                 })();
44845                 controllers.History = History;
44846             })(controllers = app.controllers || (app.controllers = {}));
44847         })(app || (app = {}));
44848         var app;
44849         (function (app) {
44850             'use strict';
44851             var appName = 'zephyr';
44852             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44853             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44854                 $urlRouterProvider.otherwise('/execution');
44855                 $locationProvider.html5Mode({
44856                     enabled: true,
44857                     requireBase: false
44858                 });
44859                 $stateProvider
44860                     .state('execution', {
44861                     url: '/execution',
44862                     templateUrl: 'templates/execution.html',
44863                     controller: 'executionController',
44864                     controllerAs: 'c'
44865                 })
44866                     .state('workspace', {
44867                     url: '/workspace',
44868                     templateUrl: 'templates/workspace.html',
44869                     controller: 'workspaceController',
44870                     controllerAs: 'c'
44871                 })
44872                     .state('history', {
44873                     url: '/history',
44874                     templateUrl: 'templates/history.html',
44875                     controller: 'historyController',
44876                     controllerAs: 'c'
44877                 });
44878             });
44879             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44880             app.zephyr.service('MyModal', app.services.MyModal);
44881             app.zephyr.controller('executionController', app.controllers.Execution);
44882             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44883             app.zephyr.controller('historyController', app.controllers.History);
44884             app.zephyr.controller('commandController', app.directives.CommandController);
44885             app.zephyr.controller('optionController', app.directives.OptionController);
44886             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44887             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44888             app.zephyr.directive('command', app.directives.Command.Factory());
44889             app.zephyr.directive('option', app.directives.Option.Factory());
44890             app.zephyr.directive('directory', app.directives.Directory.Factory());
44891         })(app || (app = {}));
44892
44893
44894 /***/ },
44895 /* 17 */
44896 /***/ function(module, exports) {
44897
44898         var app;
44899         (function (app) {
44900             var declares;
44901             (function (declares) {
44902                 var CommandInfo = (function () {
44903                     function CommandInfo(name) {
44904                         this.name = name;
44905                     }
44906                     return CommandInfo;
44907                 })();
44908                 declares.CommandInfo = CommandInfo;
44909             })(declares = app.declares || (app.declares = {}));
44910         })(app || (app = {}));
44911         var app;
44912         (function (app) {
44913             var services;
44914             (function (services) {
44915                 var APIEndPoint = (function () {
44916                     function APIEndPoint($resource) {
44917                         this.$resource = $resource;
44918                     }
44919                     APIEndPoint.prototype.resource = function (endPoint) {
44920                         var customAction = {
44921                             method: 'GET',
44922                             isArray: false
44923                         };
44924                         return this.$resource(endPoint, {}, { customAction: customAction });
44925                     };
44926                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44927                         var endPoint = '/api/optionControlFile/' + command;
44928                         return this.resource(endPoint).get();
44929                     };
44930                     APIEndPoint.prototype.getFiles = function (fileId) {
44931                         var endPoint = '/api/v1/workspace';
44932                         if (fileId) {
44933                             endPoint += '/' + fileId;
44934                         }
44935                         return this.resource(endPoint).get();
44936                     };
44937                     APIEndPoint.prototype.getDirectories = function () {
44938                         var endPoint = '/api/v1/all/workspace/directory';
44939                         return this.resource(endPoint).get();
44940                     };
44941                     return APIEndPoint;
44942                 })();
44943                 services.APIEndPoint = APIEndPoint;
44944             })(services = app.services || (app.services = {}));
44945         })(app || (app = {}));
44946         var app;
44947         (function (app) {
44948             var services;
44949             (function (services) {
44950                 var MyModal = (function () {
44951                     function MyModal($uibModal) {
44952                         this.$uibModal = $uibModal;
44953                         this.modalOption = {
44954                             backdrop: true,
44955                             controller: null,
44956                             templateUrl: null,
44957                             size: null
44958                         };
44959                     }
44960                     MyModal.prototype.open = function (modalName) {
44961                         if (modalName === 'SelectCommand') {
44962                             this.modalOption.templateUrl = 'templates/select-command.html';
44963                             this.modalOption.size = 'lg';
44964                         }
44965                         return this.$uibModal.open(this.modalOption);
44966                     };
44967                     return MyModal;
44968                 })();
44969                 services.MyModal = MyModal;
44970             })(services = app.services || (app.services = {}));
44971         })(app || (app = {}));
44972         var app;
44973         (function (app) {
44974             var directives;
44975             (function (directives) {
44976                 var Command = (function () {
44977                     function Command() {
44978                         this.restrict = 'E';
44979                         this.replace = true;
44980                         this.scope = true;
44981                         this.controller = 'commandController';
44982                         this.controllerAs = 'ctrl';
44983                         this.bindToController = {
44984                             index: '=',
44985                             name: '=',
44986                             remove: '&',
44987                             list: '='
44988                         };
44989                         this.templateUrl = 'templates/command.html';
44990                     }
44991                     Command.Factory = function () {
44992                         var directive = function () {
44993                             return new Command();
44994                         };
44995                         directive.$inject = [];
44996                         return directive;
44997                     };
44998                     return Command;
44999                 })();
45000                 directives.Command = Command;
45001                 var CommandController = (function () {
45002                     function CommandController(APIEndPoint, $scope) {
45003                         this.APIEndPoint = APIEndPoint;
45004                         this.$scope = $scope;
45005                         var controller = this;
45006                         this.APIEndPoint
45007                             .getOptionControlFile('dcdFilePrint')
45008                             .$promise
45009                             .then(function (result) {
45010                             controller.options = result.info;
45011                         });
45012                         this.APIEndPoint
45013                             .getDirectories()
45014                             .$promise
45015                             .then(function (result) {
45016                             controller.dirs = result.info;
45017                         });
45018                         this.heading = "[" + this.index + "]: dcdFilePring";
45019                         this.isOpen = true;
45020                         this.$scope.$on('close', function () {
45021                             controller.isOpen = false;
45022                         });
45023                     }
45024                     CommandController.prototype.submit = function () {
45025                         var opt = [];
45026                         angular.forEach(this.options, function (option) {
45027                             var obj = {
45028                                 name: option.option,
45029                                 arguments: []
45030                             };
45031                             angular.forEach(option.arg, function (arg) {
45032                                 if (arg.input) {
45033                                     obj.arguments.push(arg.input);
45034                                 }
45035                             });
45036                             if (obj.arguments.length > 0) {
45037                                 opt.push(obj);
45038                             }
45039                         });
45040                         var execObj = {
45041                             command: this.name,
45042                             workspace: this.workspace.fileId,
45043                             option: opt
45044                         };
45045                         console.log(JSON.stringify(execObj, null, '\t'));
45046                     };
45047                     CommandController.prototype.removeMySelf = function (index) {
45048                         this.remove()(index, this.list);
45049                     };
45050                     CommandController.prototype.reloadFiles = function () {
45051                         var _this = this;
45052                         var fileId = this.workspace.fileId;
45053                         this.APIEndPoint
45054                             .getFiles(fileId)
45055                             .$promise
45056                             .then(function (result) {
45057                             var status = result.status;
45058                             if (status === 'success') {
45059                                 _this.files = result.info;
45060                             }
45061                             else {
45062                                 console.log(result.message);
45063                             }
45064                         });
45065                     };
45066                     CommandController.prototype.debug = function () {
45067                         console.log(this.files);
45068                         console.log(this.files);
45069                         console.log(this.workspace);
45070                     };
45071                     CommandController.$inject = ['APIEndPoint', '$scope'];
45072                     return CommandController;
45073                 })();
45074                 directives.CommandController = CommandController;
45075             })(directives = app.directives || (app.directives = {}));
45076         })(app || (app = {}));
45077         var app;
45078         (function (app) {
45079             var directives;
45080             (function (directives) {
45081                 var HeaderMenu = (function () {
45082                     function HeaderMenu() {
45083                         this.restrict = 'E';
45084                         this.replace = true;
45085                         this.templateUrl = 'templates/header-menu.html';
45086                     }
45087                     HeaderMenu.Factory = function () {
45088                         var directive = function () {
45089                             return new HeaderMenu();
45090                         };
45091                         return directive;
45092                     };
45093                     return HeaderMenu;
45094                 })();
45095                 directives.HeaderMenu = HeaderMenu;
45096             })(directives = app.directives || (app.directives = {}));
45097         })(app || (app = {}));
45098         var app;
45099         (function (app) {
45100             var directives;
45101             (function (directives) {
45102                 var Option = (function () {
45103                     function Option() {
45104                         this.restrict = 'E';
45105                         this.replace = true;
45106                         this.controller = 'optionController';
45107                         this.bindToController = {
45108                             info: '=',
45109                             files: '='
45110                         };
45111                         this.scope = true;
45112                         this.templateUrl = 'templates/option.html';
45113                         this.controllerAs = 'ctrl';
45114                     }
45115                     Option.Factory = function () {
45116                         var directive = function () {
45117                             return new Option();
45118                         };
45119                         directive.$inject = [];
45120                         return directive;
45121                     };
45122                     return Option;
45123                 })();
45124                 directives.Option = Option;
45125                 var OptionController = (function () {
45126                     function OptionController() {
45127                         var controller = this;
45128                         angular.forEach(controller.info.arg, function (arg) {
45129                             if (arg.initialValue) {
45130                                 if (arg.formType === 'number') {
45131                                     arg.input = parseInt(arg.initialValue);
45132                                 }
45133                                 else {
45134                                     arg.input = arg.initialValue;
45135                                 }
45136                             }
45137                         });
45138                     }
45139                     OptionController.$inject = [];
45140                     return OptionController;
45141                 })();
45142                 directives.OptionController = OptionController;
45143             })(directives = app.directives || (app.directives = {}));
45144         })(app || (app = {}));
45145         var app;
45146         (function (app) {
45147             var directives;
45148             (function (directives) {
45149                 var Directory = (function () {
45150                     function Directory() {
45151                         this.restrict = 'E';
45152                         this.replace = true;
45153                         this.controller = 'directoryController';
45154                         this.controllerAs = 'ctrl';
45155                         this.bindToController = {
45156                             info: '=',
45157                             add: '&',
45158                             list: '=',
45159                             files: '='
45160                         };
45161                         this.templateUrl = 'templates/directory.html';
45162                     }
45163                     Directory.Factory = function () {
45164                         var directive = function () {
45165                             return new Directory();
45166                         };
45167                         return directive;
45168                     };
45169                     return Directory;
45170                 })();
45171                 directives.Directory = Directory;
45172                 var DirectoryController = (function () {
45173                     function DirectoryController(APIEndPoint, $scope) {
45174                         this.APIEndPoint = APIEndPoint;
45175                         this.$scope = $scope;
45176                         var controller = this;
45177                         this.APIEndPoint
45178                             .getFiles(this.info.fileId)
45179                             .$promise
45180                             .then(function (result) {
45181                             if (result.status === 'success') {
45182                                 controller.files = result.info;
45183                                 angular.forEach(result.info, function (file) {
45184                                     if (file.fileType === '0') {
45185                                         var o = file;
45186                                         if (controller.info.path === '/') {
45187                                             o.path = '/' + file.name;
45188                                         }
45189                                         else {
45190                                             o.path = controller.info.path + '/' + file.name;
45191                                         }
45192                                         controller.add()(o, controller.list);
45193                                     }
45194                                 });
45195                             }
45196                             ;
45197                         });
45198                     }
45199                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45200                     return DirectoryController;
45201                 })();
45202                 directives.DirectoryController = DirectoryController;
45203             })(directives = app.directives || (app.directives = {}));
45204         })(app || (app = {}));
45205         var app;
45206         (function (app) {
45207             var controllers;
45208             (function (controllers) {
45209                 var Execution = (function () {
45210                     function Execution(MyModal, $scope) {
45211                         this.MyModal = MyModal;
45212                         this.$scope = $scope;
45213                         this.commandInfoList = [];
45214                     }
45215                     ;
45216                     Execution.prototype.add = function () {
45217                         this.$scope.$broadcast('close');
45218                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
45219                     };
45220                     Execution.prototype.open = function () {
45221                         var result = this.MyModal.open('SelectCommand');
45222                         console.log(result);
45223                     };
45224                     Execution.prototype.remove = function (index, list) {
45225                         list.splice(index, 1);
45226                     };
45227                     Execution.prototype.close = function () {
45228                         console.log("close");
45229                     };
45230                     Execution.$inject = ['MyModal', '$scope'];
45231                     return Execution;
45232                 })();
45233                 controllers.Execution = Execution;
45234             })(controllers = app.controllers || (app.controllers = {}));
45235         })(app || (app = {}));
45236         var app;
45237         (function (app) {
45238             var controllers;
45239             (function (controllers) {
45240                 var Workspace = (function () {
45241                     function Workspace($scope, APIEndPoint) {
45242                         this.$scope = $scope;
45243                         this.APIEndPoint = APIEndPoint;
45244                         this.directoryList = [];
45245                         var controller = this;
45246                         var directoryList = this.directoryList;
45247                         var o = {
45248                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45249                             name: '',
45250                             parentId: '',
45251                             fileType: '',
45252                             createdAt: '',
45253                             updatedAt: '',
45254                             path: '/'
45255                         };
45256                         directoryList.push(o);
45257                     }
45258                     Workspace.prototype.addDirectory = function (info, directoryList) {
45259                         directoryList.push(info);
45260                     };
45261                     Workspace.$inject = ['$scope', 'APIEndPoint'];
45262                     return Workspace;
45263                 })();
45264                 controllers.Workspace = Workspace;
45265             })(controllers = app.controllers || (app.controllers = {}));
45266         })(app || (app = {}));
45267         var app;
45268         (function (app) {
45269             var controllers;
45270             (function (controllers) {
45271                 var History = (function () {
45272                     function History($scope) {
45273                         this.page = "History";
45274                     }
45275                     History.$inject = ['$scope'];
45276                     return History;
45277                 })();
45278                 controllers.History = History;
45279             })(controllers = app.controllers || (app.controllers = {}));
45280         })(app || (app = {}));
45281         var app;
45282         (function (app) {
45283             'use strict';
45284             var appName = 'zephyr';
45285             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45286             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45287                 $urlRouterProvider.otherwise('/execution');
45288                 $locationProvider.html5Mode({
45289                     enabled: true,
45290                     requireBase: false
45291                 });
45292                 $stateProvider
45293                     .state('execution', {
45294                     url: '/execution',
45295                     templateUrl: 'templates/execution.html',
45296                     controller: 'executionController',
45297                     controllerAs: 'c'
45298                 })
45299                     .state('workspace', {
45300                     url: '/workspace',
45301                     templateUrl: 'templates/workspace.html',
45302                     controller: 'workspaceController',
45303                     controllerAs: 'c'
45304                 })
45305                     .state('history', {
45306                     url: '/history',
45307                     templateUrl: 'templates/history.html',
45308                     controller: 'historyController',
45309                     controllerAs: 'c'
45310                 });
45311             });
45312             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45313             app.zephyr.service('MyModal', app.services.MyModal);
45314             app.zephyr.controller('executionController', app.controllers.Execution);
45315             app.zephyr.controller('workspaceController', app.controllers.Workspace);
45316             app.zephyr.controller('historyController', app.controllers.History);
45317             app.zephyr.controller('commandController', app.directives.CommandController);
45318             app.zephyr.controller('optionController', app.directives.OptionController);
45319             app.zephyr.controller('directoryController', app.directives.DirectoryController);
45320             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45321             app.zephyr.directive('command', app.directives.Command.Factory());
45322             app.zephyr.directive('option', app.directives.Option.Factory());
45323             app.zephyr.directive('directory', app.directives.Directory.Factory());
45324         })(app || (app = {}));
45325
45326
45327 /***/ },
45328 /* 18 */
45329 /***/ function(module, exports) {
45330
45331         var app;
45332         (function (app) {
45333             var declares;
45334             (function (declares) {
45335                 var CommandInfo = (function () {
45336                     function CommandInfo(name) {
45337                         this.name = name;
45338                     }
45339                     return CommandInfo;
45340                 })();
45341                 declares.CommandInfo = CommandInfo;
45342             })(declares = app.declares || (app.declares = {}));
45343         })(app || (app = {}));
45344         var app;
45345         (function (app) {
45346             var services;
45347             (function (services) {
45348                 var APIEndPoint = (function () {
45349                     function APIEndPoint($resource) {
45350                         this.$resource = $resource;
45351                     }
45352                     APIEndPoint.prototype.resource = function (endPoint) {
45353                         var customAction = {
45354                             method: 'GET',
45355                             isArray: false
45356                         };
45357                         return this.$resource(endPoint, {}, { customAction: customAction });
45358                     };
45359                     APIEndPoint.prototype.getOptionControlFile = function (command) {
45360                         var endPoint = '/api/optionControlFile/' + command;
45361                         return this.resource(endPoint).get();
45362                     };
45363                     APIEndPoint.prototype.getFiles = function (fileId) {
45364                         var endPoint = '/api/v1/workspace';
45365                         if (fileId) {
45366                             endPoint += '/' + fileId;
45367                         }
45368                         return this.resource(endPoint).get();
45369                     };
45370                     APIEndPoint.prototype.getDirectories = function () {
45371                         var endPoint = '/api/v1/all/workspace/directory';
45372                         return this.resource(endPoint).get();
45373                     };
45374                     return APIEndPoint;
45375                 })();
45376                 services.APIEndPoint = APIEndPoint;
45377             })(services = app.services || (app.services = {}));
45378         })(app || (app = {}));
45379         var app;
45380         (function (app) {
45381             var services;
45382             (function (services) {
45383                 var MyModal = (function () {
45384                     function MyModal($uibModal) {
45385                         this.$uibModal = $uibModal;
45386                         this.modalOption = {
45387                             backdrop: true,
45388                             controller: null,
45389                             templateUrl: null,
45390                             size: null
45391                         };
45392                     }
45393                     MyModal.prototype.open = function (modalName) {
45394                         if (modalName === 'SelectCommand') {
45395                             this.modalOption.templateUrl = 'templates/select-command.html';
45396                             this.modalOption.size = 'lg';
45397                         }
45398                         return this.$uibModal.open(this.modalOption);
45399                     };
45400                     return MyModal;
45401                 })();
45402                 services.MyModal = MyModal;
45403             })(services = app.services || (app.services = {}));
45404         })(app || (app = {}));
45405         var app;
45406         (function (app) {
45407             var directives;
45408             (function (directives) {
45409                 var Command = (function () {
45410                     function Command() {
45411                         this.restrict = 'E';
45412                         this.replace = true;
45413                         this.scope = true;
45414                         this.controller = 'commandController';
45415                         this.controllerAs = 'ctrl';
45416                         this.bindToController = {
45417                             index: '=',
45418                             name: '=',
45419                             remove: '&',
45420                             list: '='
45421                         };
45422                         this.templateUrl = 'templates/command.html';
45423                     }
45424                     Command.Factory = function () {
45425                         var directive = function () {
45426                             return new Command();
45427                         };
45428                         directive.$inject = [];
45429                         return directive;
45430                     };
45431                     return Command;
45432                 })();
45433                 directives.Command = Command;
45434                 var CommandController = (function () {
45435                     function CommandController(APIEndPoint, $scope) {
45436                         this.APIEndPoint = APIEndPoint;
45437                         this.$scope = $scope;
45438                         var controller = this;
45439                         this.APIEndPoint
45440                             .getOptionControlFile('dcdFilePrint')
45441                             .$promise
45442                             .then(function (result) {
45443                             controller.options = result.info;
45444                         });
45445                         this.APIEndPoint
45446                             .getDirectories()
45447                             .$promise
45448                             .then(function (result) {
45449                             controller.dirs = result.info;
45450                         });
45451                         this.heading = "[" + this.index + "]: dcdFilePring";
45452                         this.isOpen = true;
45453                         this.$scope.$on('close', function () {
45454                             controller.isOpen = false;
45455                         });
45456                     }
45457                     CommandController.prototype.submit = function () {
45458                         var opt = [];
45459                         angular.forEach(this.options, function (option) {
45460                             var obj = {
45461                                 name: option.option,
45462                                 arguments: []
45463                             };
45464                             angular.forEach(option.arg, function (arg) {
45465                                 if (arg.input) {
45466                                     obj.arguments.push(arg.input);
45467                                 }
45468                             });
45469                             if (obj.arguments.length > 0) {
45470                                 opt.push(obj);
45471                             }
45472                         });
45473                         var execObj = {
45474                             command: this.name,
45475                             workspace: this.workspace.fileId,
45476                             option: opt
45477                         };
45478                         console.log(JSON.stringify(execObj, null, '\t'));
45479                     };
45480                     CommandController.prototype.removeMySelf = function (index) {
45481                         this.remove()(index, this.list);
45482                     };
45483                     CommandController.prototype.reloadFiles = function () {
45484                         var _this = this;
45485                         var fileId = this.workspace.fileId;
45486                         this.APIEndPoint
45487                             .getFiles(fileId)
45488                             .$promise
45489                             .then(function (result) {
45490                             var status = result.status;
45491                             if (status === 'success') {
45492                                 _this.files = result.info;
45493                             }
45494                             else {
45495                                 console.log(result.message);
45496                             }
45497                         });
45498                     };
45499                     CommandController.prototype.debug = function () {
45500                         console.log(this.files);
45501                         console.log(this.files);
45502                         console.log(this.workspace);
45503                     };
45504                     CommandController.$inject = ['APIEndPoint', '$scope'];
45505                     return CommandController;
45506                 })();
45507                 directives.CommandController = CommandController;
45508             })(directives = app.directives || (app.directives = {}));
45509         })(app || (app = {}));
45510         var app;
45511         (function (app) {
45512             var directives;
45513             (function (directives) {
45514                 var HeaderMenu = (function () {
45515                     function HeaderMenu() {
45516                         this.restrict = 'E';
45517                         this.replace = true;
45518                         this.templateUrl = 'templates/header-menu.html';
45519                     }
45520                     HeaderMenu.Factory = function () {
45521                         var directive = function () {
45522                             return new HeaderMenu();
45523                         };
45524                         return directive;
45525                     };
45526                     return HeaderMenu;
45527                 })();
45528                 directives.HeaderMenu = HeaderMenu;
45529             })(directives = app.directives || (app.directives = {}));
45530         })(app || (app = {}));
45531         var app;
45532         (function (app) {
45533             var directives;
45534             (function (directives) {
45535                 var Option = (function () {
45536                     function Option() {
45537                         this.restrict = 'E';
45538                         this.replace = true;
45539                         this.controller = 'optionController';
45540                         this.bindToController = {
45541                             info: '=',
45542                             files: '='
45543                         };
45544                         this.scope = true;
45545                         this.templateUrl = 'templates/option.html';
45546                         this.controllerAs = 'ctrl';
45547                     }
45548                     Option.Factory = function () {
45549                         var directive = function () {
45550                             return new Option();
45551                         };
45552                         directive.$inject = [];
45553                         return directive;
45554                     };
45555                     return Option;
45556                 })();
45557                 directives.Option = Option;
45558                 var OptionController = (function () {
45559                     function OptionController() {
45560                         var controller = this;
45561                         angular.forEach(controller.info.arg, function (arg) {
45562                             if (arg.initialValue) {
45563                                 if (arg.formType === 'number') {
45564                                     arg.input = parseInt(arg.initialValue);
45565                                 }
45566                                 else {
45567                                     arg.input = arg.initialValue;
45568                                 }
45569                             }
45570                         });
45571                     }
45572                     OptionController.$inject = [];
45573                     return OptionController;
45574                 })();
45575                 directives.OptionController = OptionController;
45576             })(directives = app.directives || (app.directives = {}));
45577         })(app || (app = {}));
45578         var app;
45579         (function (app) {
45580             var directives;
45581             (function (directives) {
45582                 var Directory = (function () {
45583                     function Directory() {
45584                         this.restrict = 'E';
45585                         this.replace = true;
45586                         this.controller = 'directoryController';
45587                         this.controllerAs = 'ctrl';
45588                         this.bindToController = {
45589                             info: '=',
45590                             add: '&',
45591                             list: '=',
45592                             files: '='
45593                         };
45594                         this.templateUrl = 'templates/directory.html';
45595                     }
45596                     Directory.Factory = function () {
45597                         var directive = function () {
45598                             return new Directory();
45599                         };
45600                         return directive;
45601                     };
45602                     return Directory;
45603                 })();
45604                 directives.Directory = Directory;
45605                 var DirectoryController = (function () {
45606                     function DirectoryController(APIEndPoint, $scope) {
45607                         this.APIEndPoint = APIEndPoint;
45608                         this.$scope = $scope;
45609                         var controller = this;
45610                         this.APIEndPoint
45611                             .getFiles(this.info.fileId)
45612                             .$promise
45613                             .then(function (result) {
45614                             if (result.status === 'success') {
45615                                 controller.files = result.info;
45616                                 angular.forEach(result.info, function (file) {
45617                                     if (file.fileType === '0') {
45618                                         var o = file;
45619                                         if (controller.info.path === '/') {
45620                                             o.path = '/' + file.name;
45621                                         }
45622                                         else {
45623                                             o.path = controller.info.path + '/' + file.name;
45624                                         }
45625                                         controller.add()(o, controller.list);
45626                                     }
45627                                 });
45628                             }
45629                             ;
45630                         });
45631                     }
45632                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45633                     return DirectoryController;
45634                 })();
45635                 directives.DirectoryController = DirectoryController;
45636             })(directives = app.directives || (app.directives = {}));
45637         })(app || (app = {}));
45638         var app;
45639         (function (app) {
45640             var controllers;
45641             (function (controllers) {
45642                 var Execution = (function () {
45643                     function Execution(MyModal, $scope) {
45644                         this.MyModal = MyModal;
45645                         this.$scope = $scope;
45646                         this.commandInfoList = [];
45647                     }
45648                     ;
45649                     Execution.prototype.add = function () {
45650                         this.$scope.$broadcast('close');
45651                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
45652                     };
45653                     Execution.prototype.open = function () {
45654                         var result = this.MyModal.open('SelectCommand');
45655                         console.log(result);
45656                     };
45657                     Execution.prototype.remove = function (index, list) {
45658                         list.splice(index, 1);
45659                     };
45660                     Execution.prototype.close = function () {
45661                         console.log("close");
45662                     };
45663                     Execution.$inject = ['MyModal', '$scope'];
45664                     return Execution;
45665                 })();
45666                 controllers.Execution = Execution;
45667             })(controllers = app.controllers || (app.controllers = {}));
45668         })(app || (app = {}));
45669         var app;
45670         (function (app) {
45671             var controllers;
45672             (function (controllers) {
45673                 var Workspace = (function () {
45674                     function Workspace($scope, APIEndPoint) {
45675                         this.$scope = $scope;
45676                         this.APIEndPoint = APIEndPoint;
45677                         this.directoryList = [];
45678                         var controller = this;
45679                         var directoryList = this.directoryList;
45680                         var o = {
45681                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45682                             name: '',
45683                             parentId: '',
45684                             fileType: '',
45685                             createdAt: '',
45686                             updatedAt: '',
45687                             path: '/'
45688                         };
45689                         directoryList.push(o);
45690                     }
45691                     Workspace.prototype.addDirectory = function (info, directoryList) {
45692                         directoryList.push(info);
45693                     };
45694                     Workspace.$inject = ['$scope', 'APIEndPoint'];
45695                     return Workspace;
45696                 })();
45697                 controllers.Workspace = Workspace;
45698             })(controllers = app.controllers || (app.controllers = {}));
45699         })(app || (app = {}));
45700         var app;
45701         (function (app) {
45702             var controllers;
45703             (function (controllers) {
45704                 var History = (function () {
45705                     function History($scope) {
45706                         this.page = "History";
45707                     }
45708                     History.$inject = ['$scope'];
45709                     return History;
45710                 })();
45711                 controllers.History = History;
45712             })(controllers = app.controllers || (app.controllers = {}));
45713         })(app || (app = {}));
45714         var app;
45715         (function (app) {
45716             'use strict';
45717             var appName = 'zephyr';
45718             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45719             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45720                 $urlRouterProvider.otherwise('/execution');
45721                 $locationProvider.html5Mode({
45722                     enabled: true,
45723                     requireBase: false
45724                 });
45725                 $stateProvider
45726                     .state('execution', {
45727                     url: '/execution',
45728                     templateUrl: 'templates/execution.html',
45729                     controller: 'executionController',
45730                     controllerAs: 'c'
45731                 })
45732                     .state('workspace', {
45733                     url: '/workspace',
45734                     templateUrl: 'templates/workspace.html',
45735                     controller: 'workspaceController',
45736                     controllerAs: 'c'
45737                 })
45738                     .state('history', {
45739                     url: '/history',
45740                     templateUrl: 'templates/history.html',
45741                     controller: 'historyController',
45742                     controllerAs: 'c'
45743                 });
45744             });
45745             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45746             app.zephyr.service('MyModal', app.services.MyModal);
45747             app.zephyr.controller('executionController', app.controllers.Execution);
45748             app.zephyr.controller('workspaceController', app.controllers.Workspace);
45749             app.zephyr.controller('historyController', app.controllers.History);
45750             app.zephyr.controller('commandController', app.directives.CommandController);
45751             app.zephyr.controller('optionController', app.directives.OptionController);
45752             app.zephyr.controller('directoryController', app.directives.DirectoryController);
45753             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45754             app.zephyr.directive('command', app.directives.Command.Factory());
45755             app.zephyr.directive('option', app.directives.Option.Factory());
45756             app.zephyr.directive('directory', app.directives.Directory.Factory());
45757         })(app || (app = {}));
45758
45759
45760 /***/ }
45761 /******/ ]);