Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
15747 anikendra 1
/**
2
 * @license AngularJS v1.3.13
3
 * (c) 2010-2014 Google, Inc. http://angularjs.org
4
 * License: MIT
5
 */
6
(function(window, document, undefined) {'use strict';
7
 
8
/**
9
 * @description
10
 *
11
 * This object provides a utility for producing rich Error messages within
12
 * Angular. It can be called as follows:
13
 *
14
 * var exampleMinErr = minErr('example');
15
 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16
 *
17
 * The above creates an instance of minErr in the example namespace. The
18
 * resulting error will have a namespaced error code of example.one.  The
19
 * resulting error will replace {0} with the value of foo, and {1} with the
20
 * value of bar. The object is not restricted in the number of arguments it can
21
 * take.
22
 *
23
 * If fewer arguments are specified than necessary for interpolation, the extra
24
 * interpolation markers will be preserved in the final string.
25
 *
26
 * Since data will be parsed statically during a build step, some restrictions
27
 * are applied with respect to how minErr instances are created and called.
28
 * Instances should have names of the form namespaceMinErr for a minErr created
29
 * using minErr('namespace') . Error codes, namespaces and template strings
30
 * should all be static strings, not variables or general expressions.
31
 *
32
 * @param {string} module The namespace to use for the new minErr instance.
33
 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34
 *   error from returned function, for cases when a particular type of error is useful.
35
 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36
 */
37
 
38
function minErr(module, ErrorConstructor) {
39
  ErrorConstructor = ErrorConstructor || Error;
40
  return function() {
41
    var code = arguments[0],
42
      prefix = '[' + (module ? module + ':' : '') + code + '] ',
43
      template = arguments[1],
44
      templateArgs = arguments,
45
 
46
      message, i;
47
 
48
    message = prefix + template.replace(/\{\d+\}/g, function(match) {
49
      var index = +match.slice(1, -1), arg;
50
 
51
      if (index + 2 < templateArgs.length) {
52
        return toDebugString(templateArgs[index + 2]);
53
      }
54
      return match;
55
    });
56
 
57
    message = message + '\nhttp://errors.angularjs.org/1.3.13/' +
58
      (module ? module + '/' : '') + code;
59
    for (i = 2; i < arguments.length; i++) {
60
      message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
61
        encodeURIComponent(toDebugString(arguments[i]));
62
    }
63
    return new ErrorConstructor(message);
64
  };
65
}
66
 
67
/* We need to tell jshint what variables are being exported */
68
/* global angular: true,
69
  msie: true,
70
  jqLite: true,
71
  jQuery: true,
72
  slice: true,
73
  splice: true,
74
  push: true,
75
  toString: true,
76
  ngMinErr: true,
77
  angularModule: true,
78
  uid: true,
79
  REGEX_STRING_REGEXP: true,
80
  VALIDITY_STATE_PROPERTY: true,
81
 
82
  lowercase: true,
83
  uppercase: true,
84
  manualLowercase: true,
85
  manualUppercase: true,
86
  nodeName_: true,
87
  isArrayLike: true,
88
  forEach: true,
89
  sortedKeys: true,
90
  forEachSorted: true,
91
  reverseParams: true,
92
  nextUid: true,
93
  setHashKey: true,
94
  extend: true,
95
  int: true,
96
  inherit: true,
97
  noop: true,
98
  identity: true,
99
  valueFn: true,
100
  isUndefined: true,
101
  isDefined: true,
102
  isObject: true,
103
  isString: true,
104
  isNumber: true,
105
  isDate: true,
106
  isArray: true,
107
  isFunction: true,
108
  isRegExp: true,
109
  isWindow: true,
110
  isScope: true,
111
  isFile: true,
112
  isFormData: true,
113
  isBlob: true,
114
  isBoolean: true,
115
  isPromiseLike: true,
116
  trim: true,
117
  escapeForRegexp: true,
118
  isElement: true,
119
  makeMap: true,
120
  includes: true,
121
  arrayRemove: true,
122
  copy: true,
123
  shallowCopy: true,
124
  equals: true,
125
  csp: true,
126
  concat: true,
127
  sliceArgs: true,
128
  bind: true,
129
  toJsonReplacer: true,
130
  toJson: true,
131
  fromJson: true,
132
  startingTag: true,
133
  tryDecodeURIComponent: true,
134
  parseKeyValue: true,
135
  toKeyValue: true,
136
  encodeUriSegment: true,
137
  encodeUriQuery: true,
138
  angularInit: true,
139
  bootstrap: true,
140
  getTestability: true,
141
  snake_case: true,
142
  bindJQuery: true,
143
  assertArg: true,
144
  assertArgFn: true,
145
  assertNotHasOwnProperty: true,
146
  getter: true,
147
  getBlockNodes: true,
148
  hasOwnProperty: true,
149
  createMap: true,
150
 
151
  NODE_TYPE_ELEMENT: true,
152
  NODE_TYPE_TEXT: true,
153
  NODE_TYPE_COMMENT: true,
154
  NODE_TYPE_DOCUMENT: true,
155
  NODE_TYPE_DOCUMENT_FRAGMENT: true,
156
*/
157
 
158
////////////////////////////////////
159
 
160
/**
161
 * @ngdoc module
162
 * @name ng
163
 * @module ng
164
 * @description
165
 *
166
 * # ng (core module)
167
 * The ng module is loaded by default when an AngularJS application is started. The module itself
168
 * contains the essential components for an AngularJS application to function. The table below
169
 * lists a high level breakdown of each of the services/factories, filters, directives and testing
170
 * components available within this core module.
171
 *
172
 * <div doc-module-components="ng"></div>
173
 */
174
 
175
var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
176
 
177
// The name of a form control's ValidityState property.
178
// This is used so that it's possible for internal tests to create mock ValidityStates.
179
var VALIDITY_STATE_PROPERTY = 'validity';
180
 
181
/**
182
 * @ngdoc function
183
 * @name angular.lowercase
184
 * @module ng
185
 * @kind function
186
 *
187
 * @description Converts the specified string to lowercase.
188
 * @param {string} string String to be converted to lowercase.
189
 * @returns {string} Lowercased string.
190
 */
191
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
192
var hasOwnProperty = Object.prototype.hasOwnProperty;
193
 
194
/**
195
 * @ngdoc function
196
 * @name angular.uppercase
197
 * @module ng
198
 * @kind function
199
 *
200
 * @description Converts the specified string to uppercase.
201
 * @param {string} string String to be converted to uppercase.
202
 * @returns {string} Uppercased string.
203
 */
204
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
205
 
206
 
207
var manualLowercase = function(s) {
208
  /* jshint bitwise: false */
209
  return isString(s)
210
      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
211
      : s;
212
};
213
var manualUppercase = function(s) {
214
  /* jshint bitwise: false */
215
  return isString(s)
216
      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
217
      : s;
218
};
219
 
220
 
221
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
222
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
223
// with correct but slower alternatives.
224
if ('i' !== 'I'.toLowerCase()) {
225
  lowercase = manualLowercase;
226
  uppercase = manualUppercase;
227
}
228
 
229
 
230
var
231
    msie,             // holds major version number for IE, or NaN if UA is not IE.
232
    jqLite,           // delay binding since jQuery could be loaded after us.
233
    jQuery,           // delay binding
234
    slice             = [].slice,
235
    splice            = [].splice,
236
    push              = [].push,
237
    toString          = Object.prototype.toString,
238
    ngMinErr          = minErr('ng'),
239
 
240
    /** @name angular */
241
    angular           = window.angular || (window.angular = {}),
242
    angularModule,
243
    uid               = 0;
244
 
245
/**
246
 * documentMode is an IE-only property
247
 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
248
 */
249
msie = document.documentMode;
250
 
251
 
252
/**
253
 * @private
254
 * @param {*} obj
255
 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
256
 *                   String ...)
257
 */
258
function isArrayLike(obj) {
259
  if (obj == null || isWindow(obj)) {
260
    return false;
261
  }
262
 
263
  var length = obj.length;
264
 
265
  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
266
    return true;
267
  }
268
 
269
  return isString(obj) || isArray(obj) || length === 0 ||
270
         typeof length === 'number' && length > 0 && (length - 1) in obj;
271
}
272
 
273
/**
274
 * @ngdoc function
275
 * @name angular.forEach
276
 * @module ng
277
 * @kind function
278
 *
279
 * @description
280
 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
281
 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
282
 * is the value of an object property or an array element, `key` is the object property key or
283
 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
284
 *
285
 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
286
 * using the `hasOwnProperty` method.
287
 *
288
 * Unlike ES262's
289
 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
290
 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
291
 * return the value provided.
292
 *
293
   ```js
294
     var values = {name: 'misko', gender: 'male'};
295
     var log = [];
296
     angular.forEach(values, function(value, key) {
297
       this.push(key + ': ' + value);
298
     }, log);
299
     expect(log).toEqual(['name: misko', 'gender: male']);
300
   ```
301
 *
302
 * @param {Object|Array} obj Object to iterate over.
303
 * @param {Function} iterator Iterator function.
304
 * @param {Object=} context Object to become context (`this`) for the iterator function.
305
 * @returns {Object|Array} Reference to `obj`.
306
 */
307
 
308
function forEach(obj, iterator, context) {
309
  var key, length;
310
  if (obj) {
311
    if (isFunction(obj)) {
312
      for (key in obj) {
313
        // Need to check if hasOwnProperty exists,
314
        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
315
        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
316
          iterator.call(context, obj[key], key, obj);
317
        }
318
      }
319
    } else if (isArray(obj) || isArrayLike(obj)) {
320
      var isPrimitive = typeof obj !== 'object';
321
      for (key = 0, length = obj.length; key < length; key++) {
322
        if (isPrimitive || key in obj) {
323
          iterator.call(context, obj[key], key, obj);
324
        }
325
      }
326
    } else if (obj.forEach && obj.forEach !== forEach) {
327
        obj.forEach(iterator, context, obj);
328
    } else {
329
      for (key in obj) {
330
        if (obj.hasOwnProperty(key)) {
331
          iterator.call(context, obj[key], key, obj);
332
        }
333
      }
334
    }
335
  }
336
  return obj;
337
}
338
 
339
function sortedKeys(obj) {
340
  return Object.keys(obj).sort();
341
}
342
 
343
function forEachSorted(obj, iterator, context) {
344
  var keys = sortedKeys(obj);
345
  for (var i = 0; i < keys.length; i++) {
346
    iterator.call(context, obj[keys[i]], keys[i]);
347
  }
348
  return keys;
349
}
350
 
351
 
352
/**
353
 * when using forEach the params are value, key, but it is often useful to have key, value.
354
 * @param {function(string, *)} iteratorFn
355
 * @returns {function(*, string)}
356
 */
357
function reverseParams(iteratorFn) {
358
  return function(value, key) { iteratorFn(key, value); };
359
}
360
 
361
/**
362
 * A consistent way of creating unique IDs in angular.
363
 *
364
 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
365
 * we hit number precision issues in JavaScript.
366
 *
367
 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
368
 *
369
 * @returns {number} an unique alpha-numeric string
370
 */
371
function nextUid() {
372
  return ++uid;
373
}
374
 
375
 
376
/**
377
 * Set or clear the hashkey for an object.
378
 * @param obj object
379
 * @param h the hashkey (!truthy to delete the hashkey)
380
 */
381
function setHashKey(obj, h) {
382
  if (h) {
383
    obj.$$hashKey = h;
384
  } else {
385
    delete obj.$$hashKey;
386
  }
387
}
388
 
389
/**
390
 * @ngdoc function
391
 * @name angular.extend
392
 * @module ng
393
 * @kind function
394
 *
395
 * @description
396
 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
397
 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
398
 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
399
 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
400
 *
401
 * @param {Object} dst Destination object.
402
 * @param {...Object} src Source object(s).
403
 * @returns {Object} Reference to `dst`.
404
 */
405
function extend(dst) {
406
  var h = dst.$$hashKey;
407
 
408
  for (var i = 1, ii = arguments.length; i < ii; i++) {
409
    var obj = arguments[i];
410
    if (obj) {
411
      var keys = Object.keys(obj);
412
      for (var j = 0, jj = keys.length; j < jj; j++) {
413
        var key = keys[j];
414
        dst[key] = obj[key];
415
      }
416
    }
417
  }
418
 
419
  setHashKey(dst, h);
420
  return dst;
421
}
422
 
423
function int(str) {
424
  return parseInt(str, 10);
425
}
426
 
427
 
428
function inherit(parent, extra) {
429
  return extend(Object.create(parent), extra);
430
}
431
 
432
/**
433
 * @ngdoc function
434
 * @name angular.noop
435
 * @module ng
436
 * @kind function
437
 *
438
 * @description
439
 * A function that performs no operations. This function can be useful when writing code in the
440
 * functional style.
441
   ```js
442
     function foo(callback) {
443
       var result = calculateResult();
444
       (callback || angular.noop)(result);
445
     }
446
   ```
447
 */
448
function noop() {}
449
noop.$inject = [];
450
 
451
 
452
/**
453
 * @ngdoc function
454
 * @name angular.identity
455
 * @module ng
456
 * @kind function
457
 *
458
 * @description
459
 * A function that returns its first argument. This function is useful when writing code in the
460
 * functional style.
461
 *
462
   ```js
463
     function transformer(transformationFn, value) {
464
       return (transformationFn || angular.identity)(value);
465
     };
466
   ```
467
  * @param {*} value to be returned.
468
  * @returns {*} the value passed in.
469
 */
470
function identity($) {return $;}
471
identity.$inject = [];
472
 
473
 
474
function valueFn(value) {return function() {return value;};}
475
 
476
/**
477
 * @ngdoc function
478
 * @name angular.isUndefined
479
 * @module ng
480
 * @kind function
481
 *
482
 * @description
483
 * Determines if a reference is undefined.
484
 *
485
 * @param {*} value Reference to check.
486
 * @returns {boolean} True if `value` is undefined.
487
 */
488
function isUndefined(value) {return typeof value === 'undefined';}
489
 
490
 
491
/**
492
 * @ngdoc function
493
 * @name angular.isDefined
494
 * @module ng
495
 * @kind function
496
 *
497
 * @description
498
 * Determines if a reference is defined.
499
 *
500
 * @param {*} value Reference to check.
501
 * @returns {boolean} True if `value` is defined.
502
 */
503
function isDefined(value) {return typeof value !== 'undefined';}
504
 
505
 
506
/**
507
 * @ngdoc function
508
 * @name angular.isObject
509
 * @module ng
510
 * @kind function
511
 *
512
 * @description
513
 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
514
 * considered to be objects. Note that JavaScript arrays are objects.
515
 *
516
 * @param {*} value Reference to check.
517
 * @returns {boolean} True if `value` is an `Object` but not `null`.
518
 */
519
function isObject(value) {
520
  // http://jsperf.com/isobject4
521
  return value !== null && typeof value === 'object';
522
}
523
 
524
 
525
/**
526
 * @ngdoc function
527
 * @name angular.isString
528
 * @module ng
529
 * @kind function
530
 *
531
 * @description
532
 * Determines if a reference is a `String`.
533
 *
534
 * @param {*} value Reference to check.
535
 * @returns {boolean} True if `value` is a `String`.
536
 */
537
function isString(value) {return typeof value === 'string';}
538
 
539
 
540
/**
541
 * @ngdoc function
542
 * @name angular.isNumber
543
 * @module ng
544
 * @kind function
545
 *
546
 * @description
547
 * Determines if a reference is a `Number`.
548
 *
549
 * @param {*} value Reference to check.
550
 * @returns {boolean} True if `value` is a `Number`.
551
 */
552
function isNumber(value) {return typeof value === 'number';}
553
 
554
 
555
/**
556
 * @ngdoc function
557
 * @name angular.isDate
558
 * @module ng
559
 * @kind function
560
 *
561
 * @description
562
 * Determines if a value is a date.
563
 *
564
 * @param {*} value Reference to check.
565
 * @returns {boolean} True if `value` is a `Date`.
566
 */
567
function isDate(value) {
568
  return toString.call(value) === '[object Date]';
569
}
570
 
571
 
572
/**
573
 * @ngdoc function
574
 * @name angular.isArray
575
 * @module ng
576
 * @kind function
577
 *
578
 * @description
579
 * Determines if a reference is an `Array`.
580
 *
581
 * @param {*} value Reference to check.
582
 * @returns {boolean} True if `value` is an `Array`.
583
 */
584
var isArray = Array.isArray;
585
 
586
/**
587
 * @ngdoc function
588
 * @name angular.isFunction
589
 * @module ng
590
 * @kind function
591
 *
592
 * @description
593
 * Determines if a reference is a `Function`.
594
 *
595
 * @param {*} value Reference to check.
596
 * @returns {boolean} True if `value` is a `Function`.
597
 */
598
function isFunction(value) {return typeof value === 'function';}
599
 
600
 
601
/**
602
 * Determines if a value is a regular expression object.
603
 *
604
 * @private
605
 * @param {*} value Reference to check.
606
 * @returns {boolean} True if `value` is a `RegExp`.
607
 */
608
function isRegExp(value) {
609
  return toString.call(value) === '[object RegExp]';
610
}
611
 
612
 
613
/**
614
 * Checks if `obj` is a window object.
615
 *
616
 * @private
617
 * @param {*} obj Object to check
618
 * @returns {boolean} True if `obj` is a window obj.
619
 */
620
function isWindow(obj) {
621
  return obj && obj.window === obj;
622
}
623
 
624
 
625
function isScope(obj) {
626
  return obj && obj.$evalAsync && obj.$watch;
627
}
628
 
629
 
630
function isFile(obj) {
631
  return toString.call(obj) === '[object File]';
632
}
633
 
634
 
635
function isFormData(obj) {
636
  return toString.call(obj) === '[object FormData]';
637
}
638
 
639
 
640
function isBlob(obj) {
641
  return toString.call(obj) === '[object Blob]';
642
}
643
 
644
 
645
function isBoolean(value) {
646
  return typeof value === 'boolean';
647
}
648
 
649
 
650
function isPromiseLike(obj) {
651
  return obj && isFunction(obj.then);
652
}
653
 
654
 
655
var trim = function(value) {
656
  return isString(value) ? value.trim() : value;
657
};
658
 
659
// Copied from:
660
// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
661
// Prereq: s is a string.
662
var escapeForRegexp = function(s) {
663
  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
664
           replace(/\x08/g, '\\x08');
665
};
666
 
667
 
668
/**
669
 * @ngdoc function
670
 * @name angular.isElement
671
 * @module ng
672
 * @kind function
673
 *
674
 * @description
675
 * Determines if a reference is a DOM element (or wrapped jQuery element).
676
 *
677
 * @param {*} value Reference to check.
678
 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
679
 */
680
function isElement(node) {
681
  return !!(node &&
682
    (node.nodeName  // we are a direct element
683
    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
684
}
685
 
686
/**
687
 * @param str 'key1,key2,...'
688
 * @returns {object} in the form of {key1:true, key2:true, ...}
689
 */
690
function makeMap(str) {
691
  var obj = {}, items = str.split(","), i;
692
  for (i = 0; i < items.length; i++)
693
    obj[items[i]] = true;
694
  return obj;
695
}
696
 
697
 
698
function nodeName_(element) {
699
  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
700
}
701
 
702
function includes(array, obj) {
703
  return Array.prototype.indexOf.call(array, obj) != -1;
704
}
705
 
706
function arrayRemove(array, value) {
707
  var index = array.indexOf(value);
708
  if (index >= 0)
709
    array.splice(index, 1);
710
  return value;
711
}
712
 
713
/**
714
 * @ngdoc function
715
 * @name angular.copy
716
 * @module ng
717
 * @kind function
718
 *
719
 * @description
720
 * Creates a deep copy of `source`, which should be an object or an array.
721
 *
722
 * * If no destination is supplied, a copy of the object or array is created.
723
 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
724
 *   are deleted and then all elements/properties from the source are copied to it.
725
 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
726
 * * If `source` is identical to 'destination' an exception will be thrown.
727
 *
728
 * @param {*} source The source that will be used to make a copy.
729
 *                   Can be any type, including primitives, `null`, and `undefined`.
730
 * @param {(Object|Array)=} destination Destination into which the source is copied. If
731
 *     provided, must be of the same type as `source`.
732
 * @returns {*} The copy or updated `destination`, if `destination` was specified.
733
 *
734
 * @example
735
 <example module="copyExample">
736
 <file name="index.html">
737
 <div ng-controller="ExampleController">
738
 <form novalidate class="simple-form">
739
 Name: <input type="text" ng-model="user.name" /><br />
740
 E-mail: <input type="email" ng-model="user.email" /><br />
741
 Gender: <input type="radio" ng-model="user.gender" value="male" />male
742
 <input type="radio" ng-model="user.gender" value="female" />female<br />
743
 <button ng-click="reset()">RESET</button>
744
 <button ng-click="update(user)">SAVE</button>
745
 </form>
746
 <pre>form = {{user | json}}</pre>
747
 <pre>master = {{master | json}}</pre>
748
 </div>
749
 
750
 <script>
751
  angular.module('copyExample', [])
752
    .controller('ExampleController', ['$scope', function($scope) {
753
      $scope.master= {};
754
 
755
      $scope.update = function(user) {
756
        // Example with 1 argument
757
        $scope.master= angular.copy(user);
758
      };
759
 
760
      $scope.reset = function() {
761
        // Example with 2 arguments
762
        angular.copy($scope.master, $scope.user);
763
      };
764
 
765
      $scope.reset();
766
    }]);
767
 </script>
768
 </file>
769
 </example>
770
 */
771
function copy(source, destination, stackSource, stackDest) {
772
  if (isWindow(source) || isScope(source)) {
773
    throw ngMinErr('cpws',
774
      "Can't copy! Making copies of Window or Scope instances is not supported.");
775
  }
776
 
777
  if (!destination) {
778
    destination = source;
779
    if (source) {
780
      if (isArray(source)) {
781
        destination = copy(source, [], stackSource, stackDest);
782
      } else if (isDate(source)) {
783
        destination = new Date(source.getTime());
784
      } else if (isRegExp(source)) {
785
        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
786
        destination.lastIndex = source.lastIndex;
787
      } else if (isObject(source)) {
788
        var emptyObject = Object.create(Object.getPrototypeOf(source));
789
        destination = copy(source, emptyObject, stackSource, stackDest);
790
      }
791
    }
792
  } else {
793
    if (source === destination) throw ngMinErr('cpi',
794
      "Can't copy! Source and destination are identical.");
795
 
796
    stackSource = stackSource || [];
797
    stackDest = stackDest || [];
798
 
799
    if (isObject(source)) {
800
      var index = stackSource.indexOf(source);
801
      if (index !== -1) return stackDest[index];
802
 
803
      stackSource.push(source);
804
      stackDest.push(destination);
805
    }
806
 
807
    var result;
808
    if (isArray(source)) {
809
      destination.length = 0;
810
      for (var i = 0; i < source.length; i++) {
811
        result = copy(source[i], null, stackSource, stackDest);
812
        if (isObject(source[i])) {
813
          stackSource.push(source[i]);
814
          stackDest.push(result);
815
        }
816
        destination.push(result);
817
      }
818
    } else {
819
      var h = destination.$$hashKey;
820
      if (isArray(destination)) {
821
        destination.length = 0;
822
      } else {
823
        forEach(destination, function(value, key) {
824
          delete destination[key];
825
        });
826
      }
827
      for (var key in source) {
828
        if (source.hasOwnProperty(key)) {
829
          result = copy(source[key], null, stackSource, stackDest);
830
          if (isObject(source[key])) {
831
            stackSource.push(source[key]);
832
            stackDest.push(result);
833
          }
834
          destination[key] = result;
835
        }
836
      }
837
      setHashKey(destination,h);
838
    }
839
 
840
  }
841
  return destination;
842
}
843
 
844
/**
845
 * Creates a shallow copy of an object, an array or a primitive.
846
 *
847
 * Assumes that there are no proto properties for objects.
848
 */
849
function shallowCopy(src, dst) {
850
  if (isArray(src)) {
851
    dst = dst || [];
852
 
853
    for (var i = 0, ii = src.length; i < ii; i++) {
854
      dst[i] = src[i];
855
    }
856
  } else if (isObject(src)) {
857
    dst = dst || {};
858
 
859
    for (var key in src) {
860
      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
861
        dst[key] = src[key];
862
      }
863
    }
864
  }
865
 
866
  return dst || src;
867
}
868
 
869
 
870
/**
871
 * @ngdoc function
872
 * @name angular.equals
873
 * @module ng
874
 * @kind function
875
 *
876
 * @description
877
 * Determines if two objects or two values are equivalent. Supports value types, regular
878
 * expressions, arrays and objects.
879
 *
880
 * Two objects or values are considered equivalent if at least one of the following is true:
881
 *
882
 * * Both objects or values pass `===` comparison.
883
 * * Both objects or values are of the same type and all of their properties are equal by
884
 *   comparing them with `angular.equals`.
885
 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
886
 * * Both values represent the same regular expression (In JavaScript,
887
 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
888
 *   representation matches).
889
 *
890
 * During a property comparison, properties of `function` type and properties with names
891
 * that begin with `$` are ignored.
892
 *
893
 * Scope and DOMWindow objects are being compared only by identify (`===`).
894
 *
895
 * @param {*} o1 Object or value to compare.
896
 * @param {*} o2 Object or value to compare.
897
 * @returns {boolean} True if arguments are equal.
898
 */
899
function equals(o1, o2) {
900
  if (o1 === o2) return true;
901
  if (o1 === null || o2 === null) return false;
902
  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
903
  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
904
  if (t1 == t2) {
905
    if (t1 == 'object') {
906
      if (isArray(o1)) {
907
        if (!isArray(o2)) return false;
908
        if ((length = o1.length) == o2.length) {
909
          for (key = 0; key < length; key++) {
910
            if (!equals(o1[key], o2[key])) return false;
911
          }
912
          return true;
913
        }
914
      } else if (isDate(o1)) {
915
        if (!isDate(o2)) return false;
916
        return equals(o1.getTime(), o2.getTime());
917
      } else if (isRegExp(o1) && isRegExp(o2)) {
918
        return o1.toString() == o2.toString();
919
      } else {
920
        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
921
        keySet = {};
922
        for (key in o1) {
923
          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
924
          if (!equals(o1[key], o2[key])) return false;
925
          keySet[key] = true;
926
        }
927
        for (key in o2) {
928
          if (!keySet.hasOwnProperty(key) &&
929
              key.charAt(0) !== '$' &&
930
              o2[key] !== undefined &&
931
              !isFunction(o2[key])) return false;
932
        }
933
        return true;
934
      }
935
    }
936
  }
937
  return false;
938
}
939
 
940
var csp = function() {
941
  if (isDefined(csp.isActive_)) return csp.isActive_;
942
 
943
  var active = !!(document.querySelector('[ng-csp]') ||
944
                  document.querySelector('[data-ng-csp]'));
945
 
946
  if (!active) {
947
    try {
948
      /* jshint -W031, -W054 */
949
      new Function('');
950
      /* jshint +W031, +W054 */
951
    } catch (e) {
952
      active = true;
953
    }
954
  }
955
 
956
  return (csp.isActive_ = active);
957
};
958
 
959
 
960
 
961
function concat(array1, array2, index) {
962
  return array1.concat(slice.call(array2, index));
963
}
964
 
965
function sliceArgs(args, startIndex) {
966
  return slice.call(args, startIndex || 0);
967
}
968
 
969
 
970
/* jshint -W101 */
971
/**
972
 * @ngdoc function
973
 * @name angular.bind
974
 * @module ng
975
 * @kind function
976
 *
977
 * @description
978
 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
979
 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
980
 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
981
 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
982
 *
983
 * @param {Object} self Context which `fn` should be evaluated in.
984
 * @param {function()} fn Function to be bound.
985
 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
986
 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
987
 */
988
/* jshint +W101 */
989
function bind(self, fn) {
990
  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
991
  if (isFunction(fn) && !(fn instanceof RegExp)) {
992
    return curryArgs.length
993
      ? function() {
994
          return arguments.length
995
            ? fn.apply(self, concat(curryArgs, arguments, 0))
996
            : fn.apply(self, curryArgs);
997
        }
998
      : function() {
999
          return arguments.length
1000
            ? fn.apply(self, arguments)
1001
            : fn.call(self);
1002
        };
1003
  } else {
1004
    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1005
    return fn;
1006
  }
1007
}
1008
 
1009
 
1010
function toJsonReplacer(key, value) {
1011
  var val = value;
1012
 
1013
  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1014
    val = undefined;
1015
  } else if (isWindow(value)) {
1016
    val = '$WINDOW';
1017
  } else if (value &&  document === value) {
1018
    val = '$DOCUMENT';
1019
  } else if (isScope(value)) {
1020
    val = '$SCOPE';
1021
  }
1022
 
1023
  return val;
1024
}
1025
 
1026
 
1027
/**
1028
 * @ngdoc function
1029
 * @name angular.toJson
1030
 * @module ng
1031
 * @kind function
1032
 *
1033
 * @description
1034
 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1035
 * stripped since angular uses this notation internally.
1036
 *
1037
 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1038
 * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.
1039
 *    If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).
1040
 * @returns {string|undefined} JSON-ified string representing `obj`.
1041
 */
1042
function toJson(obj, pretty) {
1043
  if (typeof obj === 'undefined') return undefined;
1044
  if (!isNumber(pretty)) {
1045
    pretty = pretty ? 2 : null;
1046
  }
1047
  return JSON.stringify(obj, toJsonReplacer, pretty);
1048
}
1049
 
1050
 
1051
/**
1052
 * @ngdoc function
1053
 * @name angular.fromJson
1054
 * @module ng
1055
 * @kind function
1056
 *
1057
 * @description
1058
 * Deserializes a JSON string.
1059
 *
1060
 * @param {string} json JSON string to deserialize.
1061
 * @returns {Object|Array|string|number} Deserialized JSON string.
1062
 */
1063
function fromJson(json) {
1064
  return isString(json)
1065
      ? JSON.parse(json)
1066
      : json;
1067
}
1068
 
1069
 
1070
/**
1071
 * @returns {string} Returns the string representation of the element.
1072
 */
1073
function startingTag(element) {
1074
  element = jqLite(element).clone();
1075
  try {
1076
    // turns out IE does not let you set .html() on elements which
1077
    // are not allowed to have children. So we just ignore it.
1078
    element.empty();
1079
  } catch (e) {}
1080
  var elemHtml = jqLite('<div>').append(element).html();
1081
  try {
1082
    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1083
        elemHtml.
1084
          match(/^(<[^>]+>)/)[1].
1085
          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1086
  } catch (e) {
1087
    return lowercase(elemHtml);
1088
  }
1089
 
1090
}
1091
 
1092
 
1093
/////////////////////////////////////////////////
1094
 
1095
/**
1096
 * Tries to decode the URI component without throwing an exception.
1097
 *
1098
 * @private
1099
 * @param str value potential URI component to check.
1100
 * @returns {boolean} True if `value` can be decoded
1101
 * with the decodeURIComponent function.
1102
 */
1103
function tryDecodeURIComponent(value) {
1104
  try {
1105
    return decodeURIComponent(value);
1106
  } catch (e) {
1107
    // Ignore any invalid uri component
1108
  }
1109
}
1110
 
1111
 
1112
/**
1113
 * Parses an escaped url query string into key-value pairs.
1114
 * @returns {Object.<string,boolean|Array>}
1115
 */
1116
function parseKeyValue(/**string*/keyValue) {
1117
  var obj = {}, key_value, key;
1118
  forEach((keyValue || "").split('&'), function(keyValue) {
1119
    if (keyValue) {
1120
      key_value = keyValue.replace(/\+/g,'%20').split('=');
1121
      key = tryDecodeURIComponent(key_value[0]);
1122
      if (isDefined(key)) {
1123
        var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1124
        if (!hasOwnProperty.call(obj, key)) {
1125
          obj[key] = val;
1126
        } else if (isArray(obj[key])) {
1127
          obj[key].push(val);
1128
        } else {
1129
          obj[key] = [obj[key],val];
1130
        }
1131
      }
1132
    }
1133
  });
1134
  return obj;
1135
}
1136
 
1137
function toKeyValue(obj) {
1138
  var parts = [];
1139
  forEach(obj, function(value, key) {
1140
    if (isArray(value)) {
1141
      forEach(value, function(arrayValue) {
1142
        parts.push(encodeUriQuery(key, true) +
1143
                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1144
      });
1145
    } else {
1146
    parts.push(encodeUriQuery(key, true) +
1147
               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1148
    }
1149
  });
1150
  return parts.length ? parts.join('&') : '';
1151
}
1152
 
1153
 
1154
/**
1155
 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1156
 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1157
 * segments:
1158
 *    segment       = *pchar
1159
 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1160
 *    pct-encoded   = "%" HEXDIG HEXDIG
1161
 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1162
 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1163
 *                     / "*" / "+" / "," / ";" / "="
1164
 */
1165
function encodeUriSegment(val) {
1166
  return encodeUriQuery(val, true).
1167
             replace(/%26/gi, '&').
1168
             replace(/%3D/gi, '=').
1169
             replace(/%2B/gi, '+');
1170
}
1171
 
1172
 
1173
/**
1174
 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1175
 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1176
 * encoded per http://tools.ietf.org/html/rfc3986:
1177
 *    query       = *( pchar / "/" / "?" )
1178
 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1179
 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1180
 *    pct-encoded   = "%" HEXDIG HEXDIG
1181
 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1182
 *                     / "*" / "+" / "," / ";" / "="
1183
 */
1184
function encodeUriQuery(val, pctEncodeSpaces) {
1185
  return encodeURIComponent(val).
1186
             replace(/%40/gi, '@').
1187
             replace(/%3A/gi, ':').
1188
             replace(/%24/g, '$').
1189
             replace(/%2C/gi, ',').
1190
             replace(/%3B/gi, ';').
1191
             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1192
}
1193
 
1194
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1195
 
1196
function getNgAttribute(element, ngAttr) {
1197
  var attr, i, ii = ngAttrPrefixes.length;
1198
  element = jqLite(element);
1199
  for (i = 0; i < ii; ++i) {
1200
    attr = ngAttrPrefixes[i] + ngAttr;
1201
    if (isString(attr = element.attr(attr))) {
1202
      return attr;
1203
    }
1204
  }
1205
  return null;
1206
}
1207
 
1208
/**
1209
 * @ngdoc directive
1210
 * @name ngApp
1211
 * @module ng
1212
 *
1213
 * @element ANY
1214
 * @param {angular.Module} ngApp an optional application
1215
 *   {@link angular.module module} name to load.
1216
 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1217
 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1218
 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1219
 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1220
 *   tracking down the root of these bugs.
1221
 *
1222
 * @description
1223
 *
1224
 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1225
 * designates the **root element** of the application and is typically placed near the root element
1226
 * of the page - e.g. on the `<body>` or `<html>` tags.
1227
 *
1228
 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1229
 * found in the document will be used to define the root element to auto-bootstrap as an
1230
 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1231
 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1232
 *
1233
 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1234
 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1235
 * should contain the application code needed or have dependencies on other modules that will
1236
 * contain the code. See {@link angular.module} for more information.
1237
 *
1238
 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1239
 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1240
 * would not be resolved to `3`.
1241
 *
1242
 * `ngApp` is the easiest, and most common way to bootstrap an application.
1243
 *
1244
 <example module="ngAppDemo">
1245
   <file name="index.html">
1246
   <div ng-controller="ngAppDemoController">
1247
     I can add: {{a}} + {{b}} =  {{ a+b }}
1248
   </div>
1249
   </file>
1250
   <file name="script.js">
1251
   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1252
     $scope.a = 1;
1253
     $scope.b = 2;
1254
   });
1255
   </file>
1256
 </example>
1257
 *
1258
 * Using `ngStrictDi`, you would see something like this:
1259
 *
1260
 <example ng-app-included="true">
1261
   <file name="index.html">
1262
   <div ng-app="ngAppStrictDemo" ng-strict-di>
1263
       <div ng-controller="GoodController1">
1264
           I can add: {{a}} + {{b}} =  {{ a+b }}
1265
 
1266
           <p>This renders because the controller does not fail to
1267
              instantiate, by using explicit annotation style (see
1268
              script.js for details)
1269
           </p>
1270
       </div>
1271
 
1272
       <div ng-controller="GoodController2">
1273
           Name: <input ng-model="name"><br />
1274
           Hello, {{name}}!
1275
 
1276
           <p>This renders because the controller does not fail to
1277
              instantiate, by using explicit annotation style
1278
              (see script.js for details)
1279
           </p>
1280
       </div>
1281
 
1282
       <div ng-controller="BadController">
1283
           I can add: {{a}} + {{b}} =  {{ a+b }}
1284
 
1285
           <p>The controller could not be instantiated, due to relying
1286
              on automatic function annotations (which are disabled in
1287
              strict mode). As such, the content of this section is not
1288
              interpolated, and there should be an error in your web console.
1289
           </p>
1290
       </div>
1291
   </div>
1292
   </file>
1293
   <file name="script.js">
1294
   angular.module('ngAppStrictDemo', [])
1295
     // BadController will fail to instantiate, due to relying on automatic function annotation,
1296
     // rather than an explicit annotation
1297
     .controller('BadController', function($scope) {
1298
       $scope.a = 1;
1299
       $scope.b = 2;
1300
     })
1301
     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1302
     // due to using explicit annotations using the array style and $inject property, respectively.
1303
     .controller('GoodController1', ['$scope', function($scope) {
1304
       $scope.a = 1;
1305
       $scope.b = 2;
1306
     }])
1307
     .controller('GoodController2', GoodController2);
1308
     function GoodController2($scope) {
1309
       $scope.name = "World";
1310
     }
1311
     GoodController2.$inject = ['$scope'];
1312
   </file>
1313
   <file name="style.css">
1314
   div[ng-controller] {
1315
       margin-bottom: 1em;
1316
       -webkit-border-radius: 4px;
1317
       border-radius: 4px;
1318
       border: 1px solid;
1319
       padding: .5em;
1320
   }
1321
   div[ng-controller^=Good] {
1322
       border-color: #d6e9c6;
1323
       background-color: #dff0d8;
1324
       color: #3c763d;
1325
   }
1326
   div[ng-controller^=Bad] {
1327
       border-color: #ebccd1;
1328
       background-color: #f2dede;
1329
       color: #a94442;
1330
       margin-bottom: 0;
1331
   }
1332
   </file>
1333
 </example>
1334
 */
1335
function angularInit(element, bootstrap) {
1336
  var appElement,
1337
      module,
1338
      config = {};
1339
 
1340
  // The element `element` has priority over any other element
1341
  forEach(ngAttrPrefixes, function(prefix) {
1342
    var name = prefix + 'app';
1343
 
1344
    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1345
      appElement = element;
1346
      module = element.getAttribute(name);
1347
    }
1348
  });
1349
  forEach(ngAttrPrefixes, function(prefix) {
1350
    var name = prefix + 'app';
1351
    var candidate;
1352
 
1353
    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1354
      appElement = candidate;
1355
      module = candidate.getAttribute(name);
1356
    }
1357
  });
1358
  if (appElement) {
1359
    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1360
    bootstrap(appElement, module ? [module] : [], config);
1361
  }
1362
}
1363
 
1364
/**
1365
 * @ngdoc function
1366
 * @name angular.bootstrap
1367
 * @module ng
1368
 * @description
1369
 * Use this function to manually start up angular application.
1370
 *
1371
 * See: {@link guide/bootstrap Bootstrap}
1372
 *
1373
 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1374
 * They must use {@link ng.directive:ngApp ngApp}.
1375
 *
1376
 * Angular will detect if it has been loaded into the browser more than once and only allow the
1377
 * first loaded script to be bootstrapped and will report a warning to the browser console for
1378
 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1379
 * multiple instances of Angular try to work on the DOM.
1380
 *
1381
 * ```html
1382
 * <!doctype html>
1383
 * <html>
1384
 * <body>
1385
 * <div ng-controller="WelcomeController">
1386
 *   {{greeting}}
1387
 * </div>
1388
 *
1389
 * <script src="angular.js"></script>
1390
 * <script>
1391
 *   var app = angular.module('demo', [])
1392
 *   .controller('WelcomeController', function($scope) {
1393
 *       $scope.greeting = 'Welcome!';
1394
 *   });
1395
 *   angular.bootstrap(document, ['demo']);
1396
 * </script>
1397
 * </body>
1398
 * </html>
1399
 * ```
1400
 *
1401
 * @param {DOMElement} element DOM element which is the root of angular application.
1402
 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1403
 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1404
 *     function that will be invoked by the injector as a `config` block.
1405
 *     See: {@link angular.module modules}
1406
 * @param {Object=} config an object for defining configuration options for the application. The
1407
 *     following keys are supported:
1408
 *
1409
 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1410
 *   assist in finding bugs which break minified code. Defaults to `false`.
1411
 *
1412
 * @returns {auto.$injector} Returns the newly created injector for this app.
1413
 */
1414
function bootstrap(element, modules, config) {
1415
  if (!isObject(config)) config = {};
1416
  var defaultConfig = {
1417
    strictDi: false
1418
  };
1419
  config = extend(defaultConfig, config);
1420
  var doBootstrap = function() {
1421
    element = jqLite(element);
1422
 
1423
    if (element.injector()) {
1424
      var tag = (element[0] === document) ? 'document' : startingTag(element);
1425
      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1426
      throw ngMinErr(
1427
          'btstrpd',
1428
          "App Already Bootstrapped with this Element '{0}'",
1429
          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1430
    }
1431
 
1432
    modules = modules || [];
1433
    modules.unshift(['$provide', function($provide) {
1434
      $provide.value('$rootElement', element);
1435
    }]);
1436
 
1437
    if (config.debugInfoEnabled) {
1438
      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1439
      modules.push(['$compileProvider', function($compileProvider) {
1440
        $compileProvider.debugInfoEnabled(true);
1441
      }]);
1442
    }
1443
 
1444
    modules.unshift('ng');
1445
    var injector = createInjector(modules, config.strictDi);
1446
    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1447
       function bootstrapApply(scope, element, compile, injector) {
1448
        scope.$apply(function() {
1449
          element.data('$injector', injector);
1450
          compile(element)(scope);
1451
        });
1452
      }]
1453
    );
1454
    return injector;
1455
  };
1456
 
1457
  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1458
  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1459
 
1460
  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1461
    config.debugInfoEnabled = true;
1462
    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1463
  }
1464
 
1465
  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1466
    return doBootstrap();
1467
  }
1468
 
1469
  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1470
  angular.resumeBootstrap = function(extraModules) {
1471
    forEach(extraModules, function(module) {
1472
      modules.push(module);
1473
    });
1474
    return doBootstrap();
1475
  };
1476
 
1477
  if (isFunction(angular.resumeDeferredBootstrap)) {
1478
    angular.resumeDeferredBootstrap();
1479
  }
1480
}
1481
 
1482
/**
1483
 * @ngdoc function
1484
 * @name angular.reloadWithDebugInfo
1485
 * @module ng
1486
 * @description
1487
 * Use this function to reload the current application with debug information turned on.
1488
 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1489
 *
1490
 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1491
 */
1492
function reloadWithDebugInfo() {
1493
  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1494
  window.location.reload();
1495
}
1496
 
1497
/**
1498
 * @name angular.getTestability
1499
 * @module ng
1500
 * @description
1501
 * Get the testability service for the instance of Angular on the given
1502
 * element.
1503
 * @param {DOMElement} element DOM element which is the root of angular application.
1504
 */
1505
function getTestability(rootElement) {
1506
  var injector = angular.element(rootElement).injector();
1507
  if (!injector) {
1508
    throw ngMinErr('test',
1509
      'no injector found for element argument to getTestability');
1510
  }
1511
  return injector.get('$$testability');
1512
}
1513
 
1514
var SNAKE_CASE_REGEXP = /[A-Z]/g;
1515
function snake_case(name, separator) {
1516
  separator = separator || '_';
1517
  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1518
    return (pos ? separator : '') + letter.toLowerCase();
1519
  });
1520
}
1521
 
1522
var bindJQueryFired = false;
1523
var skipDestroyOnNextJQueryCleanData;
1524
function bindJQuery() {
1525
  var originalCleanData;
1526
 
1527
  if (bindJQueryFired) {
1528
    return;
1529
  }
1530
 
1531
  // bind to jQuery if present;
1532
  jQuery = window.jQuery;
1533
  // Use jQuery if it exists with proper functionality, otherwise default to us.
1534
  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1535
  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1536
  // versions. It will not work for sure with jQuery <1.7, though.
1537
  if (jQuery && jQuery.fn.on) {
1538
    jqLite = jQuery;
1539
    extend(jQuery.fn, {
1540
      scope: JQLitePrototype.scope,
1541
      isolateScope: JQLitePrototype.isolateScope,
1542
      controller: JQLitePrototype.controller,
1543
      injector: JQLitePrototype.injector,
1544
      inheritedData: JQLitePrototype.inheritedData
1545
    });
1546
 
1547
    // All nodes removed from the DOM via various jQuery APIs like .remove()
1548
    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1549
    // the $destroy event on all removed nodes.
1550
    originalCleanData = jQuery.cleanData;
1551
    jQuery.cleanData = function(elems) {
1552
      var events;
1553
      if (!skipDestroyOnNextJQueryCleanData) {
1554
        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1555
          events = jQuery._data(elem, "events");
1556
          if (events && events.$destroy) {
1557
            jQuery(elem).triggerHandler('$destroy');
1558
          }
1559
        }
1560
      } else {
1561
        skipDestroyOnNextJQueryCleanData = false;
1562
      }
1563
      originalCleanData(elems);
1564
    };
1565
  } else {
1566
    jqLite = JQLite;
1567
  }
1568
 
1569
  angular.element = jqLite;
1570
 
1571
  // Prevent double-proxying.
1572
  bindJQueryFired = true;
1573
}
1574
 
1575
/**
1576
 * throw error if the argument is falsy.
1577
 */
1578
function assertArg(arg, name, reason) {
1579
  if (!arg) {
1580
    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1581
  }
1582
  return arg;
1583
}
1584
 
1585
function assertArgFn(arg, name, acceptArrayAnnotation) {
1586
  if (acceptArrayAnnotation && isArray(arg)) {
1587
      arg = arg[arg.length - 1];
1588
  }
1589
 
1590
  assertArg(isFunction(arg), name, 'not a function, got ' +
1591
      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1592
  return arg;
1593
}
1594
 
1595
/**
1596
 * throw error if the name given is hasOwnProperty
1597
 * @param  {String} name    the name to test
1598
 * @param  {String} context the context in which the name is used, such as module or directive
1599
 */
1600
function assertNotHasOwnProperty(name, context) {
1601
  if (name === 'hasOwnProperty') {
1602
    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1603
  }
1604
}
1605
 
1606
/**
1607
 * Return the value accessible from the object by path. Any undefined traversals are ignored
1608
 * @param {Object} obj starting object
1609
 * @param {String} path path to traverse
1610
 * @param {boolean} [bindFnToScope=true]
1611
 * @returns {Object} value as accessible by path
1612
 */
1613
//TODO(misko): this function needs to be removed
1614
function getter(obj, path, bindFnToScope) {
1615
  if (!path) return obj;
1616
  var keys = path.split('.');
1617
  var key;
1618
  var lastInstance = obj;
1619
  var len = keys.length;
1620
 
1621
  for (var i = 0; i < len; i++) {
1622
    key = keys[i];
1623
    if (obj) {
1624
      obj = (lastInstance = obj)[key];
1625
    }
1626
  }
1627
  if (!bindFnToScope && isFunction(obj)) {
1628
    return bind(lastInstance, obj);
1629
  }
1630
  return obj;
1631
}
1632
 
1633
/**
1634
 * Return the DOM siblings between the first and last node in the given array.
1635
 * @param {Array} array like object
1636
 * @returns {jqLite} jqLite collection containing the nodes
1637
 */
1638
function getBlockNodes(nodes) {
1639
  // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1640
  //             collection, otherwise update the original collection.
1641
  var node = nodes[0];
1642
  var endNode = nodes[nodes.length - 1];
1643
  var blockNodes = [node];
1644
 
1645
  do {
1646
    node = node.nextSibling;
1647
    if (!node) break;
1648
    blockNodes.push(node);
1649
  } while (node !== endNode);
1650
 
1651
  return jqLite(blockNodes);
1652
}
1653
 
1654
 
1655
/**
1656
 * Creates a new object without a prototype. This object is useful for lookup without having to
1657
 * guard against prototypically inherited properties via hasOwnProperty.
1658
 *
1659
 * Related micro-benchmarks:
1660
 * - http://jsperf.com/object-create2
1661
 * - http://jsperf.com/proto-map-lookup/2
1662
 * - http://jsperf.com/for-in-vs-object-keys2
1663
 *
1664
 * @returns {Object}
1665
 */
1666
function createMap() {
1667
  return Object.create(null);
1668
}
1669
 
1670
var NODE_TYPE_ELEMENT = 1;
1671
var NODE_TYPE_TEXT = 3;
1672
var NODE_TYPE_COMMENT = 8;
1673
var NODE_TYPE_DOCUMENT = 9;
1674
var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1675
 
1676
/**
1677
 * @ngdoc type
1678
 * @name angular.Module
1679
 * @module ng
1680
 * @description
1681
 *
1682
 * Interface for configuring angular {@link angular.module modules}.
1683
 */
1684
 
1685
function setupModuleLoader(window) {
1686
 
1687
  var $injectorMinErr = minErr('$injector');
1688
  var ngMinErr = minErr('ng');
1689
 
1690
  function ensure(obj, name, factory) {
1691
    return obj[name] || (obj[name] = factory());
1692
  }
1693
 
1694
  var angular = ensure(window, 'angular', Object);
1695
 
1696
  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1697
  angular.$$minErr = angular.$$minErr || minErr;
1698
 
1699
  return ensure(angular, 'module', function() {
1700
    /** @type {Object.<string, angular.Module>} */
1701
    var modules = {};
1702
 
1703
    /**
1704
     * @ngdoc function
1705
     * @name angular.module
1706
     * @module ng
1707
     * @description
1708
     *
1709
     * The `angular.module` is a global place for creating, registering and retrieving Angular
1710
     * modules.
1711
     * All modules (angular core or 3rd party) that should be available to an application must be
1712
     * registered using this mechanism.
1713
     *
1714
     * When passed two or more arguments, a new module is created.  If passed only one argument, an
1715
     * existing module (the name passed as the first argument to `module`) is retrieved.
1716
     *
1717
     *
1718
     * # Module
1719
     *
1720
     * A module is a collection of services, directives, controllers, filters, and configuration information.
1721
     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1722
     *
1723
     * ```js
1724
     * // Create a new module
1725
     * var myModule = angular.module('myModule', []);
1726
     *
1727
     * // register a new service
1728
     * myModule.value('appName', 'MyCoolApp');
1729
     *
1730
     * // configure existing services inside initialization blocks.
1731
     * myModule.config(['$locationProvider', function($locationProvider) {
1732
     *   // Configure existing providers
1733
     *   $locationProvider.hashPrefix('!');
1734
     * }]);
1735
     * ```
1736
     *
1737
     * Then you can create an injector and load your modules like this:
1738
     *
1739
     * ```js
1740
     * var injector = angular.injector(['ng', 'myModule'])
1741
     * ```
1742
     *
1743
     * However it's more likely that you'll just use
1744
     * {@link ng.directive:ngApp ngApp} or
1745
     * {@link angular.bootstrap} to simplify this process for you.
1746
     *
1747
     * @param {!string} name The name of the module to create or retrieve.
1748
     * @param {!Array.<string>=} requires If specified then new module is being created. If
1749
     *        unspecified then the module is being retrieved for further configuration.
1750
     * @param {Function=} configFn Optional configuration function for the module. Same as
1751
     *        {@link angular.Module#config Module#config()}.
1752
     * @returns {module} new module with the {@link angular.Module} api.
1753
     */
1754
    return function module(name, requires, configFn) {
1755
      var assertNotHasOwnProperty = function(name, context) {
1756
        if (name === 'hasOwnProperty') {
1757
          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1758
        }
1759
      };
1760
 
1761
      assertNotHasOwnProperty(name, 'module');
1762
      if (requires && modules.hasOwnProperty(name)) {
1763
        modules[name] = null;
1764
      }
1765
      return ensure(modules, name, function() {
1766
        if (!requires) {
1767
          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1768
             "the module name or forgot to load it. If registering a module ensure that you " +
1769
             "specify the dependencies as the second argument.", name);
1770
        }
1771
 
1772
        /** @type {!Array.<Array.<*>>} */
1773
        var invokeQueue = [];
1774
 
1775
        /** @type {!Array.<Function>} */
1776
        var configBlocks = [];
1777
 
1778
        /** @type {!Array.<Function>} */
1779
        var runBlocks = [];
1780
 
1781
        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1782
 
1783
        /** @type {angular.Module} */
1784
        var moduleInstance = {
1785
          // Private state
1786
          _invokeQueue: invokeQueue,
1787
          _configBlocks: configBlocks,
1788
          _runBlocks: runBlocks,
1789
 
1790
          /**
1791
           * @ngdoc property
1792
           * @name angular.Module#requires
1793
           * @module ng
1794
           *
1795
           * @description
1796
           * Holds the list of modules which the injector will load before the current module is
1797
           * loaded.
1798
           */
1799
          requires: requires,
1800
 
1801
          /**
1802
           * @ngdoc property
1803
           * @name angular.Module#name
1804
           * @module ng
1805
           *
1806
           * @description
1807
           * Name of the module.
1808
           */
1809
          name: name,
1810
 
1811
 
1812
          /**
1813
           * @ngdoc method
1814
           * @name angular.Module#provider
1815
           * @module ng
1816
           * @param {string} name service name
1817
           * @param {Function} providerType Construction function for creating new instance of the
1818
           *                                service.
1819
           * @description
1820
           * See {@link auto.$provide#provider $provide.provider()}.
1821
           */
1822
          provider: invokeLater('$provide', 'provider'),
1823
 
1824
          /**
1825
           * @ngdoc method
1826
           * @name angular.Module#factory
1827
           * @module ng
1828
           * @param {string} name service name
1829
           * @param {Function} providerFunction Function for creating new instance of the service.
1830
           * @description
1831
           * See {@link auto.$provide#factory $provide.factory()}.
1832
           */
1833
          factory: invokeLater('$provide', 'factory'),
1834
 
1835
          /**
1836
           * @ngdoc method
1837
           * @name angular.Module#service
1838
           * @module ng
1839
           * @param {string} name service name
1840
           * @param {Function} constructor A constructor function that will be instantiated.
1841
           * @description
1842
           * See {@link auto.$provide#service $provide.service()}.
1843
           */
1844
          service: invokeLater('$provide', 'service'),
1845
 
1846
          /**
1847
           * @ngdoc method
1848
           * @name angular.Module#value
1849
           * @module ng
1850
           * @param {string} name service name
1851
           * @param {*} object Service instance object.
1852
           * @description
1853
           * See {@link auto.$provide#value $provide.value()}.
1854
           */
1855
          value: invokeLater('$provide', 'value'),
1856
 
1857
          /**
1858
           * @ngdoc method
1859
           * @name angular.Module#constant
1860
           * @module ng
1861
           * @param {string} name constant name
1862
           * @param {*} object Constant value.
1863
           * @description
1864
           * Because the constant are fixed, they get applied before other provide methods.
1865
           * See {@link auto.$provide#constant $provide.constant()}.
1866
           */
1867
          constant: invokeLater('$provide', 'constant', 'unshift'),
1868
 
1869
          /**
1870
           * @ngdoc method
1871
           * @name angular.Module#animation
1872
           * @module ng
1873
           * @param {string} name animation name
1874
           * @param {Function} animationFactory Factory function for creating new instance of an
1875
           *                                    animation.
1876
           * @description
1877
           *
1878
           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1879
           *
1880
           *
1881
           * Defines an animation hook that can be later used with
1882
           * {@link ngAnimate.$animate $animate} service and directives that use this service.
1883
           *
1884
           * ```js
1885
           * module.animation('.animation-name', function($inject1, $inject2) {
1886
           *   return {
1887
           *     eventName : function(element, done) {
1888
           *       //code to run the animation
1889
           *       //once complete, then run done()
1890
           *       return function cancellationFunction(element) {
1891
           *         //code to cancel the animation
1892
           *       }
1893
           *     }
1894
           *   }
1895
           * })
1896
           * ```
1897
           *
1898
           * See {@link ng.$animateProvider#register $animateProvider.register()} and
1899
           * {@link ngAnimate ngAnimate module} for more information.
1900
           */
1901
          animation: invokeLater('$animateProvider', 'register'),
1902
 
1903
          /**
1904
           * @ngdoc method
1905
           * @name angular.Module#filter
1906
           * @module ng
1907
           * @param {string} name Filter name.
1908
           * @param {Function} filterFactory Factory function for creating new instance of filter.
1909
           * @description
1910
           * See {@link ng.$filterProvider#register $filterProvider.register()}.
1911
           */
1912
          filter: invokeLater('$filterProvider', 'register'),
1913
 
1914
          /**
1915
           * @ngdoc method
1916
           * @name angular.Module#controller
1917
           * @module ng
1918
           * @param {string|Object} name Controller name, or an object map of controllers where the
1919
           *    keys are the names and the values are the constructors.
1920
           * @param {Function} constructor Controller constructor function.
1921
           * @description
1922
           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1923
           */
1924
          controller: invokeLater('$controllerProvider', 'register'),
1925
 
1926
          /**
1927
           * @ngdoc method
1928
           * @name angular.Module#directive
1929
           * @module ng
1930
           * @param {string|Object} name Directive name, or an object map of directives where the
1931
           *    keys are the names and the values are the factories.
1932
           * @param {Function} directiveFactory Factory function for creating new instance of
1933
           * directives.
1934
           * @description
1935
           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1936
           */
1937
          directive: invokeLater('$compileProvider', 'directive'),
1938
 
1939
          /**
1940
           * @ngdoc method
1941
           * @name angular.Module#config
1942
           * @module ng
1943
           * @param {Function} configFn Execute this function on module load. Useful for service
1944
           *    configuration.
1945
           * @description
1946
           * Use this method to register work which needs to be performed on module loading.
1947
           * For more about how to configure services, see
1948
           * {@link providers#provider-recipe Provider Recipe}.
1949
           */
1950
          config: config,
1951
 
1952
          /**
1953
           * @ngdoc method
1954
           * @name angular.Module#run
1955
           * @module ng
1956
           * @param {Function} initializationFn Execute this function after injector creation.
1957
           *    Useful for application initialization.
1958
           * @description
1959
           * Use this method to register work which should be performed when the injector is done
1960
           * loading all modules.
1961
           */
1962
          run: function(block) {
1963
            runBlocks.push(block);
1964
            return this;
1965
          }
1966
        };
1967
 
1968
        if (configFn) {
1969
          config(configFn);
1970
        }
1971
 
1972
        return moduleInstance;
1973
 
1974
        /**
1975
         * @param {string} provider
1976
         * @param {string} method
1977
         * @param {String=} insertMethod
1978
         * @returns {angular.Module}
1979
         */
1980
        function invokeLater(provider, method, insertMethod, queue) {
1981
          if (!queue) queue = invokeQueue;
1982
          return function() {
1983
            queue[insertMethod || 'push']([provider, method, arguments]);
1984
            return moduleInstance;
1985
          };
1986
        }
1987
      });
1988
    };
1989
  });
1990
 
1991
}
1992
 
1993
/* global: toDebugString: true */
1994
 
1995
function serializeObject(obj) {
1996
  var seen = [];
1997
 
1998
  return JSON.stringify(obj, function(key, val) {
1999
    val = toJsonReplacer(key, val);
2000
    if (isObject(val)) {
2001
 
2002
      if (seen.indexOf(val) >= 0) return '<<already seen>>';
2003
 
2004
      seen.push(val);
2005
    }
2006
    return val;
2007
  });
2008
}
2009
 
2010
function toDebugString(obj) {
2011
  if (typeof obj === 'function') {
2012
    return obj.toString().replace(/ \{[\s\S]*$/, '');
2013
  } else if (typeof obj === 'undefined') {
2014
    return 'undefined';
2015
  } else if (typeof obj !== 'string') {
2016
    return serializeObject(obj);
2017
  }
2018
  return obj;
2019
}
2020
 
2021
/* global angularModule: true,
2022
  version: true,
2023
 
2024
  $LocaleProvider,
2025
  $CompileProvider,
2026
 
2027
  htmlAnchorDirective,
2028
  inputDirective,
2029
  inputDirective,
2030
  formDirective,
2031
  scriptDirective,
2032
  selectDirective,
2033
  styleDirective,
2034
  optionDirective,
2035
  ngBindDirective,
2036
  ngBindHtmlDirective,
2037
  ngBindTemplateDirective,
2038
  ngClassDirective,
2039
  ngClassEvenDirective,
2040
  ngClassOddDirective,
2041
  ngCspDirective,
2042
  ngCloakDirective,
2043
  ngControllerDirective,
2044
  ngFormDirective,
2045
  ngHideDirective,
2046
  ngIfDirective,
2047
  ngIncludeDirective,
2048
  ngIncludeFillContentDirective,
2049
  ngInitDirective,
2050
  ngNonBindableDirective,
2051
  ngPluralizeDirective,
2052
  ngRepeatDirective,
2053
  ngShowDirective,
2054
  ngStyleDirective,
2055
  ngSwitchDirective,
2056
  ngSwitchWhenDirective,
2057
  ngSwitchDefaultDirective,
2058
  ngOptionsDirective,
2059
  ngTranscludeDirective,
2060
  ngModelDirective,
2061
  ngListDirective,
2062
  ngChangeDirective,
2063
  patternDirective,
2064
  patternDirective,
2065
  requiredDirective,
2066
  requiredDirective,
2067
  minlengthDirective,
2068
  minlengthDirective,
2069
  maxlengthDirective,
2070
  maxlengthDirective,
2071
  ngValueDirective,
2072
  ngModelOptionsDirective,
2073
  ngAttributeAliasDirectives,
2074
  ngEventDirectives,
2075
 
2076
  $AnchorScrollProvider,
2077
  $AnimateProvider,
2078
  $BrowserProvider,
2079
  $CacheFactoryProvider,
2080
  $ControllerProvider,
2081
  $DocumentProvider,
2082
  $ExceptionHandlerProvider,
2083
  $FilterProvider,
2084
  $InterpolateProvider,
2085
  $IntervalProvider,
2086
  $HttpProvider,
2087
  $HttpBackendProvider,
2088
  $LocationProvider,
2089
  $LogProvider,
2090
  $ParseProvider,
2091
  $RootScopeProvider,
2092
  $QProvider,
2093
  $$QProvider,
2094
  $$SanitizeUriProvider,
2095
  $SceProvider,
2096
  $SceDelegateProvider,
2097
  $SnifferProvider,
2098
  $TemplateCacheProvider,
2099
  $TemplateRequestProvider,
2100
  $$TestabilityProvider,
2101
  $TimeoutProvider,
2102
  $$RAFProvider,
2103
  $$AsyncCallbackProvider,
2104
  $WindowProvider,
2105
  $$jqLiteProvider
2106
*/
2107
 
2108
 
2109
/**
2110
 * @ngdoc object
2111
 * @name angular.version
2112
 * @module ng
2113
 * @description
2114
 * An object that contains information about the current AngularJS version. This object has the
2115
 * following properties:
2116
 *
2117
 * - `full` – `{string}` – Full version string, such as "0.9.18".
2118
 * - `major` – `{number}` – Major version number, such as "0".
2119
 * - `minor` – `{number}` – Minor version number, such as "9".
2120
 * - `dot` – `{number}` – Dot version number, such as "18".
2121
 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2122
 */
2123
var version = {
2124
  full: '1.3.13',    // all of these placeholder strings will be replaced by grunt's
2125
  major: 1,    // package task
2126
  minor: 3,
2127
  dot: 13,
2128
  codeName: 'meticulous-riffleshuffle'
2129
};
2130
 
2131
 
2132
function publishExternalAPI(angular) {
2133
  extend(angular, {
2134
    'bootstrap': bootstrap,
2135
    'copy': copy,
2136
    'extend': extend,
2137
    'equals': equals,
2138
    'element': jqLite,
2139
    'forEach': forEach,
2140
    'injector': createInjector,
2141
    'noop': noop,
2142
    'bind': bind,
2143
    'toJson': toJson,
2144
    'fromJson': fromJson,
2145
    'identity': identity,
2146
    'isUndefined': isUndefined,
2147
    'isDefined': isDefined,
2148
    'isString': isString,
2149
    'isFunction': isFunction,
2150
    'isObject': isObject,
2151
    'isNumber': isNumber,
2152
    'isElement': isElement,
2153
    'isArray': isArray,
2154
    'version': version,
2155
    'isDate': isDate,
2156
    'lowercase': lowercase,
2157
    'uppercase': uppercase,
2158
    'callbacks': {counter: 0},
2159
    'getTestability': getTestability,
2160
    '$$minErr': minErr,
2161
    '$$csp': csp,
2162
    'reloadWithDebugInfo': reloadWithDebugInfo
2163
  });
2164
 
2165
  angularModule = setupModuleLoader(window);
2166
  try {
2167
    angularModule('ngLocale');
2168
  } catch (e) {
2169
    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2170
  }
2171
 
2172
  angularModule('ng', ['ngLocale'], ['$provide',
2173
    function ngModule($provide) {
2174
      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2175
      $provide.provider({
2176
        $$sanitizeUri: $$SanitizeUriProvider
2177
      });
2178
      $provide.provider('$compile', $CompileProvider).
2179
        directive({
2180
            a: htmlAnchorDirective,
2181
            input: inputDirective,
2182
            textarea: inputDirective,
2183
            form: formDirective,
2184
            script: scriptDirective,
2185
            select: selectDirective,
2186
            style: styleDirective,
2187
            option: optionDirective,
2188
            ngBind: ngBindDirective,
2189
            ngBindHtml: ngBindHtmlDirective,
2190
            ngBindTemplate: ngBindTemplateDirective,
2191
            ngClass: ngClassDirective,
2192
            ngClassEven: ngClassEvenDirective,
2193
            ngClassOdd: ngClassOddDirective,
2194
            ngCloak: ngCloakDirective,
2195
            ngController: ngControllerDirective,
2196
            ngForm: ngFormDirective,
2197
            ngHide: ngHideDirective,
2198
            ngIf: ngIfDirective,
2199
            ngInclude: ngIncludeDirective,
2200
            ngInit: ngInitDirective,
2201
            ngNonBindable: ngNonBindableDirective,
2202
            ngPluralize: ngPluralizeDirective,
2203
            ngRepeat: ngRepeatDirective,
2204
            ngShow: ngShowDirective,
2205
            ngStyle: ngStyleDirective,
2206
            ngSwitch: ngSwitchDirective,
2207
            ngSwitchWhen: ngSwitchWhenDirective,
2208
            ngSwitchDefault: ngSwitchDefaultDirective,
2209
            ngOptions: ngOptionsDirective,
2210
            ngTransclude: ngTranscludeDirective,
2211
            ngModel: ngModelDirective,
2212
            ngList: ngListDirective,
2213
            ngChange: ngChangeDirective,
2214
            pattern: patternDirective,
2215
            ngPattern: patternDirective,
2216
            required: requiredDirective,
2217
            ngRequired: requiredDirective,
2218
            minlength: minlengthDirective,
2219
            ngMinlength: minlengthDirective,
2220
            maxlength: maxlengthDirective,
2221
            ngMaxlength: maxlengthDirective,
2222
            ngValue: ngValueDirective,
2223
            ngModelOptions: ngModelOptionsDirective
2224
        }).
2225
        directive({
2226
          ngInclude: ngIncludeFillContentDirective
2227
        }).
2228
        directive(ngAttributeAliasDirectives).
2229
        directive(ngEventDirectives);
2230
      $provide.provider({
2231
        $anchorScroll: $AnchorScrollProvider,
2232
        $animate: $AnimateProvider,
2233
        $browser: $BrowserProvider,
2234
        $cacheFactory: $CacheFactoryProvider,
2235
        $controller: $ControllerProvider,
2236
        $document: $DocumentProvider,
2237
        $exceptionHandler: $ExceptionHandlerProvider,
2238
        $filter: $FilterProvider,
2239
        $interpolate: $InterpolateProvider,
2240
        $interval: $IntervalProvider,
2241
        $http: $HttpProvider,
2242
        $httpBackend: $HttpBackendProvider,
2243
        $location: $LocationProvider,
2244
        $log: $LogProvider,
2245
        $parse: $ParseProvider,
2246
        $rootScope: $RootScopeProvider,
2247
        $q: $QProvider,
2248
        $$q: $$QProvider,
2249
        $sce: $SceProvider,
2250
        $sceDelegate: $SceDelegateProvider,
2251
        $sniffer: $SnifferProvider,
2252
        $templateCache: $TemplateCacheProvider,
2253
        $templateRequest: $TemplateRequestProvider,
2254
        $$testability: $$TestabilityProvider,
2255
        $timeout: $TimeoutProvider,
2256
        $window: $WindowProvider,
2257
        $$rAF: $$RAFProvider,
2258
        $$asyncCallback: $$AsyncCallbackProvider,
2259
        $$jqLite: $$jqLiteProvider
2260
      });
2261
    }
2262
  ]);
2263
}
2264
 
2265
/* global JQLitePrototype: true,
2266
  addEventListenerFn: true,
2267
  removeEventListenerFn: true,
2268
  BOOLEAN_ATTR: true,
2269
  ALIASED_ATTR: true,
2270
*/
2271
 
2272
//////////////////////////////////
2273
//JQLite
2274
//////////////////////////////////
2275
 
2276
/**
2277
 * @ngdoc function
2278
 * @name angular.element
2279
 * @module ng
2280
 * @kind function
2281
 *
2282
 * @description
2283
 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2284
 *
2285
 * If jQuery is available, `angular.element` is an alias for the
2286
 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2287
 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2288
 *
2289
 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2290
 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2291
 * commonly needed functionality with the goal of having a very small footprint.</div>
2292
 *
2293
 * To use jQuery, simply load it before `DOMContentLoaded` event fired.
2294
 *
2295
 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2296
 * jqLite; they are never raw DOM references.</div>
2297
 *
2298
 * ## Angular's jqLite
2299
 * jqLite provides only the following jQuery methods:
2300
 *
2301
 * - [`addClass()`](http://api.jquery.com/addClass/)
2302
 * - [`after()`](http://api.jquery.com/after/)
2303
 * - [`append()`](http://api.jquery.com/append/)
2304
 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2305
 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2306
 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2307
 * - [`clone()`](http://api.jquery.com/clone/)
2308
 * - [`contents()`](http://api.jquery.com/contents/)
2309
 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`
2310
 * - [`data()`](http://api.jquery.com/data/)
2311
 * - [`detach()`](http://api.jquery.com/detach/)
2312
 * - [`empty()`](http://api.jquery.com/empty/)
2313
 * - [`eq()`](http://api.jquery.com/eq/)
2314
 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2315
 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2316
 * - [`html()`](http://api.jquery.com/html/)
2317
 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2318
 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2319
 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2320
 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2321
 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2322
 * - [`prepend()`](http://api.jquery.com/prepend/)
2323
 * - [`prop()`](http://api.jquery.com/prop/)
2324
 * - [`ready()`](http://api.jquery.com/ready/)
2325
 * - [`remove()`](http://api.jquery.com/remove/)
2326
 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2327
 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2328
 * - [`removeData()`](http://api.jquery.com/removeData/)
2329
 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2330
 * - [`text()`](http://api.jquery.com/text/)
2331
 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2332
 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2333
 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2334
 * - [`val()`](http://api.jquery.com/val/)
2335
 * - [`wrap()`](http://api.jquery.com/wrap/)
2336
 *
2337
 * ## jQuery/jqLite Extras
2338
 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2339
 *
2340
 * ### Events
2341
 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2342
 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2343
 *    element before it is removed.
2344
 *
2345
 * ### Methods
2346
 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2347
 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2348
 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2349
 *   `'ngModel'`).
2350
 * - `injector()` - retrieves the injector of the current element or its parent.
2351
 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2352
 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2353
 *   be enabled.
2354
 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2355
 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2356
 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2357
 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2358
 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2359
 *   parent element is reached.
2360
 *
2361
 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2362
 * @returns {Object} jQuery object.
2363
 */
2364
 
2365
JQLite.expando = 'ng339';
2366
 
2367
var jqCache = JQLite.cache = {},
2368
    jqId = 1,
2369
    addEventListenerFn = function(element, type, fn) {
2370
      element.addEventListener(type, fn, false);
2371
    },
2372
    removeEventListenerFn = function(element, type, fn) {
2373
      element.removeEventListener(type, fn, false);
2374
    };
2375
 
2376
/*
2377
 * !!! This is an undocumented "private" function !!!
2378
 */
2379
JQLite._data = function(node) {
2380
  //jQuery always returns an object on cache miss
2381
  return this.cache[node[this.expando]] || {};
2382
};
2383
 
2384
function jqNextId() { return ++jqId; }
2385
 
2386
 
2387
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2388
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2389
var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2390
var jqLiteMinErr = minErr('jqLite');
2391
 
2392
/**
2393
 * Converts snake_case to camelCase.
2394
 * Also there is special case for Moz prefix starting with upper case letter.
2395
 * @param name Name to normalize
2396
 */
2397
function camelCase(name) {
2398
  return name.
2399
    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2400
      return offset ? letter.toUpperCase() : letter;
2401
    }).
2402
    replace(MOZ_HACK_REGEXP, 'Moz$1');
2403
}
2404
 
2405
var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2406
var HTML_REGEXP = /<|&#?\w+;/;
2407
var TAG_NAME_REGEXP = /<([\w:]+)/;
2408
var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2409
 
2410
var wrapMap = {
2411
  'option': [1, '<select multiple="multiple">', '</select>'],
2412
 
2413
  'thead': [1, '<table>', '</table>'],
2414
  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2415
  'tr': [2, '<table><tbody>', '</tbody></table>'],
2416
  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2417
  '_default': [0, "", ""]
2418
};
2419
 
2420
wrapMap.optgroup = wrapMap.option;
2421
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2422
wrapMap.th = wrapMap.td;
2423
 
2424
 
2425
function jqLiteIsTextNode(html) {
2426
  return !HTML_REGEXP.test(html);
2427
}
2428
 
2429
function jqLiteAcceptsData(node) {
2430
  // The window object can accept data but has no nodeType
2431
  // Otherwise we are only interested in elements (1) and documents (9)
2432
  var nodeType = node.nodeType;
2433
  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2434
}
2435
 
2436
function jqLiteBuildFragment(html, context) {
2437
  var tmp, tag, wrap,
2438
      fragment = context.createDocumentFragment(),
2439
      nodes = [], i;
2440
 
2441
  if (jqLiteIsTextNode(html)) {
2442
    // Convert non-html into a text node
2443
    nodes.push(context.createTextNode(html));
2444
  } else {
2445
    // Convert html into DOM nodes
2446
    tmp = tmp || fragment.appendChild(context.createElement("div"));
2447
    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2448
    wrap = wrapMap[tag] || wrapMap._default;
2449
    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2450
 
2451
    // Descend through wrappers to the right content
2452
    i = wrap[0];
2453
    while (i--) {
2454
      tmp = tmp.lastChild;
2455
    }
2456
 
2457
    nodes = concat(nodes, tmp.childNodes);
2458
 
2459
    tmp = fragment.firstChild;
2460
    tmp.textContent = "";
2461
  }
2462
 
2463
  // Remove wrapper from fragment
2464
  fragment.textContent = "";
2465
  fragment.innerHTML = ""; // Clear inner HTML
2466
  forEach(nodes, function(node) {
2467
    fragment.appendChild(node);
2468
  });
2469
 
2470
  return fragment;
2471
}
2472
 
2473
function jqLiteParseHTML(html, context) {
2474
  context = context || document;
2475
  var parsed;
2476
 
2477
  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2478
    return [context.createElement(parsed[1])];
2479
  }
2480
 
2481
  if ((parsed = jqLiteBuildFragment(html, context))) {
2482
    return parsed.childNodes;
2483
  }
2484
 
2485
  return [];
2486
}
2487
 
2488
/////////////////////////////////////////////
2489
function JQLite(element) {
2490
  if (element instanceof JQLite) {
2491
    return element;
2492
  }
2493
 
2494
  var argIsString;
2495
 
2496
  if (isString(element)) {
2497
    element = trim(element);
2498
    argIsString = true;
2499
  }
2500
  if (!(this instanceof JQLite)) {
2501
    if (argIsString && element.charAt(0) != '<') {
2502
      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2503
    }
2504
    return new JQLite(element);
2505
  }
2506
 
2507
  if (argIsString) {
2508
    jqLiteAddNodes(this, jqLiteParseHTML(element));
2509
  } else {
2510
    jqLiteAddNodes(this, element);
2511
  }
2512
}
2513
 
2514
function jqLiteClone(element) {
2515
  return element.cloneNode(true);
2516
}
2517
 
2518
function jqLiteDealoc(element, onlyDescendants) {
2519
  if (!onlyDescendants) jqLiteRemoveData(element);
2520
 
2521
  if (element.querySelectorAll) {
2522
    var descendants = element.querySelectorAll('*');
2523
    for (var i = 0, l = descendants.length; i < l; i++) {
2524
      jqLiteRemoveData(descendants[i]);
2525
    }
2526
  }
2527
}
2528
 
2529
function jqLiteOff(element, type, fn, unsupported) {
2530
  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2531
 
2532
  var expandoStore = jqLiteExpandoStore(element);
2533
  var events = expandoStore && expandoStore.events;
2534
  var handle = expandoStore && expandoStore.handle;
2535
 
2536
  if (!handle) return; //no listeners registered
2537
 
2538
  if (!type) {
2539
    for (type in events) {
2540
      if (type !== '$destroy') {
2541
        removeEventListenerFn(element, type, handle);
2542
      }
2543
      delete events[type];
2544
    }
2545
  } else {
2546
    forEach(type.split(' '), function(type) {
2547
      if (isDefined(fn)) {
2548
        var listenerFns = events[type];
2549
        arrayRemove(listenerFns || [], fn);
2550
        if (listenerFns && listenerFns.length > 0) {
2551
          return;
2552
        }
2553
      }
2554
 
2555
      removeEventListenerFn(element, type, handle);
2556
      delete events[type];
2557
    });
2558
  }
2559
}
2560
 
2561
function jqLiteRemoveData(element, name) {
2562
  var expandoId = element.ng339;
2563
  var expandoStore = expandoId && jqCache[expandoId];
2564
 
2565
  if (expandoStore) {
2566
    if (name) {
2567
      delete expandoStore.data[name];
2568
      return;
2569
    }
2570
 
2571
    if (expandoStore.handle) {
2572
      if (expandoStore.events.$destroy) {
2573
        expandoStore.handle({}, '$destroy');
2574
      }
2575
      jqLiteOff(element);
2576
    }
2577
    delete jqCache[expandoId];
2578
    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2579
  }
2580
}
2581
 
2582
 
2583
function jqLiteExpandoStore(element, createIfNecessary) {
2584
  var expandoId = element.ng339,
2585
      expandoStore = expandoId && jqCache[expandoId];
2586
 
2587
  if (createIfNecessary && !expandoStore) {
2588
    element.ng339 = expandoId = jqNextId();
2589
    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2590
  }
2591
 
2592
  return expandoStore;
2593
}
2594
 
2595
 
2596
function jqLiteData(element, key, value) {
2597
  if (jqLiteAcceptsData(element)) {
2598
 
2599
    var isSimpleSetter = isDefined(value);
2600
    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2601
    var massGetter = !key;
2602
    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2603
    var data = expandoStore && expandoStore.data;
2604
 
2605
    if (isSimpleSetter) { // data('key', value)
2606
      data[key] = value;
2607
    } else {
2608
      if (massGetter) {  // data()
2609
        return data;
2610
      } else {
2611
        if (isSimpleGetter) { // data('key')
2612
          // don't force creation of expandoStore if it doesn't exist yet
2613
          return data && data[key];
2614
        } else { // mass-setter: data({key1: val1, key2: val2})
2615
          extend(data, key);
2616
        }
2617
      }
2618
    }
2619
  }
2620
}
2621
 
2622
function jqLiteHasClass(element, selector) {
2623
  if (!element.getAttribute) return false;
2624
  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2625
      indexOf(" " + selector + " ") > -1);
2626
}
2627
 
2628
function jqLiteRemoveClass(element, cssClasses) {
2629
  if (cssClasses && element.setAttribute) {
2630
    forEach(cssClasses.split(' '), function(cssClass) {
2631
      element.setAttribute('class', trim(
2632
          (" " + (element.getAttribute('class') || '') + " ")
2633
          .replace(/[\n\t]/g, " ")
2634
          .replace(" " + trim(cssClass) + " ", " "))
2635
      );
2636
    });
2637
  }
2638
}
2639
 
2640
function jqLiteAddClass(element, cssClasses) {
2641
  if (cssClasses && element.setAttribute) {
2642
    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2643
                            .replace(/[\n\t]/g, " ");
2644
 
2645
    forEach(cssClasses.split(' '), function(cssClass) {
2646
      cssClass = trim(cssClass);
2647
      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2648
        existingClasses += cssClass + ' ';
2649
      }
2650
    });
2651
 
2652
    element.setAttribute('class', trim(existingClasses));
2653
  }
2654
}
2655
 
2656
 
2657
function jqLiteAddNodes(root, elements) {
2658
  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2659
 
2660
  if (elements) {
2661
 
2662
    // if a Node (the most common case)
2663
    if (elements.nodeType) {
2664
      root[root.length++] = elements;
2665
    } else {
2666
      var length = elements.length;
2667
 
2668
      // if an Array or NodeList and not a Window
2669
      if (typeof length === 'number' && elements.window !== elements) {
2670
        if (length) {
2671
          for (var i = 0; i < length; i++) {
2672
            root[root.length++] = elements[i];
2673
          }
2674
        }
2675
      } else {
2676
        root[root.length++] = elements;
2677
      }
2678
    }
2679
  }
2680
}
2681
 
2682
 
2683
function jqLiteController(element, name) {
2684
  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2685
}
2686
 
2687
function jqLiteInheritedData(element, name, value) {
2688
  // if element is the document object work with the html element instead
2689
  // this makes $(document).scope() possible
2690
  if (element.nodeType == NODE_TYPE_DOCUMENT) {
2691
    element = element.documentElement;
2692
  }
2693
  var names = isArray(name) ? name : [name];
2694
 
2695
  while (element) {
2696
    for (var i = 0, ii = names.length; i < ii; i++) {
2697
      if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2698
    }
2699
 
2700
    // If dealing with a document fragment node with a host element, and no parent, use the host
2701
    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2702
    // to lookup parent controllers.
2703
    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2704
  }
2705
}
2706
 
2707
function jqLiteEmpty(element) {
2708
  jqLiteDealoc(element, true);
2709
  while (element.firstChild) {
2710
    element.removeChild(element.firstChild);
2711
  }
2712
}
2713
 
2714
function jqLiteRemove(element, keepData) {
2715
  if (!keepData) jqLiteDealoc(element);
2716
  var parent = element.parentNode;
2717
  if (parent) parent.removeChild(element);
2718
}
2719
 
2720
 
2721
function jqLiteDocumentLoaded(action, win) {
2722
  win = win || window;
2723
  if (win.document.readyState === 'complete') {
2724
    // Force the action to be run async for consistent behaviour
2725
    // from the action's point of view
2726
    // i.e. it will definitely not be in a $apply
2727
    win.setTimeout(action);
2728
  } else {
2729
    // No need to unbind this handler as load is only ever called once
2730
    jqLite(win).on('load', action);
2731
  }
2732
}
2733
 
2734
//////////////////////////////////////////
2735
// Functions which are declared directly.
2736
//////////////////////////////////////////
2737
var JQLitePrototype = JQLite.prototype = {
2738
  ready: function(fn) {
2739
    var fired = false;
2740
 
2741
    function trigger() {
2742
      if (fired) return;
2743
      fired = true;
2744
      fn();
2745
    }
2746
 
2747
    // check if document is already loaded
2748
    if (document.readyState === 'complete') {
2749
      setTimeout(trigger);
2750
    } else {
2751
      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2752
      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2753
      // jshint -W064
2754
      JQLite(window).on('load', trigger); // fallback to window.onload for others
2755
      // jshint +W064
2756
    }
2757
  },
2758
  toString: function() {
2759
    var value = [];
2760
    forEach(this, function(e) { value.push('' + e);});
2761
    return '[' + value.join(', ') + ']';
2762
  },
2763
 
2764
  eq: function(index) {
2765
      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2766
  },
2767
 
2768
  length: 0,
2769
  push: push,
2770
  sort: [].sort,
2771
  splice: [].splice
2772
};
2773
 
2774
//////////////////////////////////////////
2775
// Functions iterating getter/setters.
2776
// these functions return self on setter and
2777
// value on get.
2778
//////////////////////////////////////////
2779
var BOOLEAN_ATTR = {};
2780
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2781
  BOOLEAN_ATTR[lowercase(value)] = value;
2782
});
2783
var BOOLEAN_ELEMENTS = {};
2784
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2785
  BOOLEAN_ELEMENTS[value] = true;
2786
});
2787
var ALIASED_ATTR = {
2788
  'ngMinlength': 'minlength',
2789
  'ngMaxlength': 'maxlength',
2790
  'ngMin': 'min',
2791
  'ngMax': 'max',
2792
  'ngPattern': 'pattern'
2793
};
2794
 
2795
function getBooleanAttrName(element, name) {
2796
  // check dom last since we will most likely fail on name
2797
  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2798
 
2799
  // booleanAttr is here twice to minimize DOM access
2800
  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
2801
}
2802
 
2803
function getAliasedAttrName(element, name) {
2804
  var nodeName = element.nodeName;
2805
  return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2806
}
2807
 
2808
forEach({
2809
  data: jqLiteData,
2810
  removeData: jqLiteRemoveData
2811
}, function(fn, name) {
2812
  JQLite[name] = fn;
2813
});
2814
 
2815
forEach({
2816
  data: jqLiteData,
2817
  inheritedData: jqLiteInheritedData,
2818
 
2819
  scope: function(element) {
2820
    // Can't use jqLiteData here directly so we stay compatible with jQuery!
2821
    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2822
  },
2823
 
2824
  isolateScope: function(element) {
2825
    // Can't use jqLiteData here directly so we stay compatible with jQuery!
2826
    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2827
  },
2828
 
2829
  controller: jqLiteController,
2830
 
2831
  injector: function(element) {
2832
    return jqLiteInheritedData(element, '$injector');
2833
  },
2834
 
2835
  removeAttr: function(element, name) {
2836
    element.removeAttribute(name);
2837
  },
2838
 
2839
  hasClass: jqLiteHasClass,
2840
 
2841
  css: function(element, name, value) {
2842
    name = camelCase(name);
2843
 
2844
    if (isDefined(value)) {
2845
      element.style[name] = value;
2846
    } else {
2847
      return element.style[name];
2848
    }
2849
  },
2850
 
2851
  attr: function(element, name, value) {
2852
    var lowercasedName = lowercase(name);
2853
    if (BOOLEAN_ATTR[lowercasedName]) {
2854
      if (isDefined(value)) {
2855
        if (!!value) {
2856
          element[name] = true;
2857
          element.setAttribute(name, lowercasedName);
2858
        } else {
2859
          element[name] = false;
2860
          element.removeAttribute(lowercasedName);
2861
        }
2862
      } else {
2863
        return (element[name] ||
2864
                 (element.attributes.getNamedItem(name) || noop).specified)
2865
               ? lowercasedName
2866
               : undefined;
2867
      }
2868
    } else if (isDefined(value)) {
2869
      element.setAttribute(name, value);
2870
    } else if (element.getAttribute) {
2871
      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2872
      // some elements (e.g. Document) don't have get attribute, so return undefined
2873
      var ret = element.getAttribute(name, 2);
2874
      // normalize non-existing attributes to undefined (as jQuery)
2875
      return ret === null ? undefined : ret;
2876
    }
2877
  },
2878
 
2879
  prop: function(element, name, value) {
2880
    if (isDefined(value)) {
2881
      element[name] = value;
2882
    } else {
2883
      return element[name];
2884
    }
2885
  },
2886
 
2887
  text: (function() {
2888
    getText.$dv = '';
2889
    return getText;
2890
 
2891
    function getText(element, value) {
2892
      if (isUndefined(value)) {
2893
        var nodeType = element.nodeType;
2894
        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
2895
      }
2896
      element.textContent = value;
2897
    }
2898
  })(),
2899
 
2900
  val: function(element, value) {
2901
    if (isUndefined(value)) {
2902
      if (element.multiple && nodeName_(element) === 'select') {
2903
        var result = [];
2904
        forEach(element.options, function(option) {
2905
          if (option.selected) {
2906
            result.push(option.value || option.text);
2907
          }
2908
        });
2909
        return result.length === 0 ? null : result;
2910
      }
2911
      return element.value;
2912
    }
2913
    element.value = value;
2914
  },
2915
 
2916
  html: function(element, value) {
2917
    if (isUndefined(value)) {
2918
      return element.innerHTML;
2919
    }
2920
    jqLiteDealoc(element, true);
2921
    element.innerHTML = value;
2922
  },
2923
 
2924
  empty: jqLiteEmpty
2925
}, function(fn, name) {
2926
  /**
2927
   * Properties: writes return selection, reads return first value
2928
   */
2929
  JQLite.prototype[name] = function(arg1, arg2) {
2930
    var i, key;
2931
    var nodeCount = this.length;
2932
 
2933
    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2934
    // in a way that survives minification.
2935
    // jqLiteEmpty takes no arguments but is a setter.
2936
    if (fn !== jqLiteEmpty &&
2937
        (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2938
      if (isObject(arg1)) {
2939
 
2940
        // we are a write, but the object properties are the key/values
2941
        for (i = 0; i < nodeCount; i++) {
2942
          if (fn === jqLiteData) {
2943
            // data() takes the whole object in jQuery
2944
            fn(this[i], arg1);
2945
          } else {
2946
            for (key in arg1) {
2947
              fn(this[i], key, arg1[key]);
2948
            }
2949
          }
2950
        }
2951
        // return self for chaining
2952
        return this;
2953
      } else {
2954
        // we are a read, so read the first child.
2955
        // TODO: do we still need this?
2956
        var value = fn.$dv;
2957
        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2958
        var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2959
        for (var j = 0; j < jj; j++) {
2960
          var nodeValue = fn(this[j], arg1, arg2);
2961
          value = value ? value + nodeValue : nodeValue;
2962
        }
2963
        return value;
2964
      }
2965
    } else {
2966
      // we are a write, so apply to all children
2967
      for (i = 0; i < nodeCount; i++) {
2968
        fn(this[i], arg1, arg2);
2969
      }
2970
      // return self for chaining
2971
      return this;
2972
    }
2973
  };
2974
});
2975
 
2976
function createEventHandler(element, events) {
2977
  var eventHandler = function(event, type) {
2978
    // jQuery specific api
2979
    event.isDefaultPrevented = function() {
2980
      return event.defaultPrevented;
2981
    };
2982
 
2983
    var eventFns = events[type || event.type];
2984
    var eventFnsLength = eventFns ? eventFns.length : 0;
2985
 
2986
    if (!eventFnsLength) return;
2987
 
2988
    if (isUndefined(event.immediatePropagationStopped)) {
2989
      var originalStopImmediatePropagation = event.stopImmediatePropagation;
2990
      event.stopImmediatePropagation = function() {
2991
        event.immediatePropagationStopped = true;
2992
 
2993
        if (event.stopPropagation) {
2994
          event.stopPropagation();
2995
        }
2996
 
2997
        if (originalStopImmediatePropagation) {
2998
          originalStopImmediatePropagation.call(event);
2999
        }
3000
      };
3001
    }
3002
 
3003
    event.isImmediatePropagationStopped = function() {
3004
      return event.immediatePropagationStopped === true;
3005
    };
3006
 
3007
    // Copy event handlers in case event handlers array is modified during execution.
3008
    if ((eventFnsLength > 1)) {
3009
      eventFns = shallowCopy(eventFns);
3010
    }
3011
 
3012
    for (var i = 0; i < eventFnsLength; i++) {
3013
      if (!event.isImmediatePropagationStopped()) {
3014
        eventFns[i].call(element, event);
3015
      }
3016
    }
3017
  };
3018
 
3019
  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3020
  //       events on `element`
3021
  eventHandler.elem = element;
3022
  return eventHandler;
3023
}
3024
 
3025
//////////////////////////////////////////
3026
// Functions iterating traversal.
3027
// These functions chain results into a single
3028
// selector.
3029
//////////////////////////////////////////
3030
forEach({
3031
  removeData: jqLiteRemoveData,
3032
 
3033
  on: function jqLiteOn(element, type, fn, unsupported) {
3034
    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3035
 
3036
    // Do not add event handlers to non-elements because they will not be cleaned up.
3037
    if (!jqLiteAcceptsData(element)) {
3038
      return;
3039
    }
3040
 
3041
    var expandoStore = jqLiteExpandoStore(element, true);
3042
    var events = expandoStore.events;
3043
    var handle = expandoStore.handle;
3044
 
3045
    if (!handle) {
3046
      handle = expandoStore.handle = createEventHandler(element, events);
3047
    }
3048
 
3049
    // http://jsperf.com/string-indexof-vs-split
3050
    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3051
    var i = types.length;
3052
 
3053
    while (i--) {
3054
      type = types[i];
3055
      var eventFns = events[type];
3056
 
3057
      if (!eventFns) {
3058
        events[type] = [];
3059
 
3060
        if (type === 'mouseenter' || type === 'mouseleave') {
3061
          // Refer to jQuery's implementation of mouseenter & mouseleave
3062
          // Read about mouseenter and mouseleave:
3063
          // http://www.quirksmode.org/js/events_mouse.html#link8
3064
 
3065
          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3066
            var target = this, related = event.relatedTarget;
3067
            // For mousenter/leave call the handler if related is outside the target.
3068
            // NB: No relatedTarget if the mouse left/entered the browser window
3069
            if (!related || (related !== target && !target.contains(related))) {
3070
              handle(event, type);
3071
            }
3072
          });
3073
 
3074
        } else {
3075
          if (type !== '$destroy') {
3076
            addEventListenerFn(element, type, handle);
3077
          }
3078
        }
3079
        eventFns = events[type];
3080
      }
3081
      eventFns.push(fn);
3082
    }
3083
  },
3084
 
3085
  off: jqLiteOff,
3086
 
3087
  one: function(element, type, fn) {
3088
    element = jqLite(element);
3089
 
3090
    //add the listener twice so that when it is called
3091
    //you can remove the original function and still be
3092
    //able to call element.off(ev, fn) normally
3093
    element.on(type, function onFn() {
3094
      element.off(type, fn);
3095
      element.off(type, onFn);
3096
    });
3097
    element.on(type, fn);
3098
  },
3099
 
3100
  replaceWith: function(element, replaceNode) {
3101
    var index, parent = element.parentNode;
3102
    jqLiteDealoc(element);
3103
    forEach(new JQLite(replaceNode), function(node) {
3104
      if (index) {
3105
        parent.insertBefore(node, index.nextSibling);
3106
      } else {
3107
        parent.replaceChild(node, element);
3108
      }
3109
      index = node;
3110
    });
3111
  },
3112
 
3113
  children: function(element) {
3114
    var children = [];
3115
    forEach(element.childNodes, function(element) {
3116
      if (element.nodeType === NODE_TYPE_ELEMENT)
3117
        children.push(element);
3118
    });
3119
    return children;
3120
  },
3121
 
3122
  contents: function(element) {
3123
    return element.contentDocument || element.childNodes || [];
3124
  },
3125
 
3126
  append: function(element, node) {
3127
    var nodeType = element.nodeType;
3128
    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3129
 
3130
    node = new JQLite(node);
3131
 
3132
    for (var i = 0, ii = node.length; i < ii; i++) {
3133
      var child = node[i];
3134
      element.appendChild(child);
3135
    }
3136
  },
3137
 
3138
  prepend: function(element, node) {
3139
    if (element.nodeType === NODE_TYPE_ELEMENT) {
3140
      var index = element.firstChild;
3141
      forEach(new JQLite(node), function(child) {
3142
        element.insertBefore(child, index);
3143
      });
3144
    }
3145
  },
3146
 
3147
  wrap: function(element, wrapNode) {
3148
    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3149
    var parent = element.parentNode;
3150
    if (parent) {
3151
      parent.replaceChild(wrapNode, element);
3152
    }
3153
    wrapNode.appendChild(element);
3154
  },
3155
 
3156
  remove: jqLiteRemove,
3157
 
3158
  detach: function(element) {
3159
    jqLiteRemove(element, true);
3160
  },
3161
 
3162
  after: function(element, newElement) {
3163
    var index = element, parent = element.parentNode;
3164
    newElement = new JQLite(newElement);
3165
 
3166
    for (var i = 0, ii = newElement.length; i < ii; i++) {
3167
      var node = newElement[i];
3168
      parent.insertBefore(node, index.nextSibling);
3169
      index = node;
3170
    }
3171
  },
3172
 
3173
  addClass: jqLiteAddClass,
3174
  removeClass: jqLiteRemoveClass,
3175
 
3176
  toggleClass: function(element, selector, condition) {
3177
    if (selector) {
3178
      forEach(selector.split(' '), function(className) {
3179
        var classCondition = condition;
3180
        if (isUndefined(classCondition)) {
3181
          classCondition = !jqLiteHasClass(element, className);
3182
        }
3183
        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3184
      });
3185
    }
3186
  },
3187
 
3188
  parent: function(element) {
3189
    var parent = element.parentNode;
3190
    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3191
  },
3192
 
3193
  next: function(element) {
3194
    return element.nextElementSibling;
3195
  },
3196
 
3197
  find: function(element, selector) {
3198
    if (element.getElementsByTagName) {
3199
      return element.getElementsByTagName(selector);
3200
    } else {
3201
      return [];
3202
    }
3203
  },
3204
 
3205
  clone: jqLiteClone,
3206
 
3207
  triggerHandler: function(element, event, extraParameters) {
3208
 
3209
    var dummyEvent, eventFnsCopy, handlerArgs;
3210
    var eventName = event.type || event;
3211
    var expandoStore = jqLiteExpandoStore(element);
3212
    var events = expandoStore && expandoStore.events;
3213
    var eventFns = events && events[eventName];
3214
 
3215
    if (eventFns) {
3216
      // Create a dummy event to pass to the handlers
3217
      dummyEvent = {
3218
        preventDefault: function() { this.defaultPrevented = true; },
3219
        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3220
        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3221
        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3222
        stopPropagation: noop,
3223
        type: eventName,
3224
        target: element
3225
      };
3226
 
3227
      // If a custom event was provided then extend our dummy event with it
3228
      if (event.type) {
3229
        dummyEvent = extend(dummyEvent, event);
3230
      }
3231
 
3232
      // Copy event handlers in case event handlers array is modified during execution.
3233
      eventFnsCopy = shallowCopy(eventFns);
3234
      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3235
 
3236
      forEach(eventFnsCopy, function(fn) {
3237
        if (!dummyEvent.isImmediatePropagationStopped()) {
3238
          fn.apply(element, handlerArgs);
3239
        }
3240
      });
3241
    }
3242
  }
3243
}, function(fn, name) {
3244
  /**
3245
   * chaining functions
3246
   */
3247
  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3248
    var value;
3249
 
3250
    for (var i = 0, ii = this.length; i < ii; i++) {
3251
      if (isUndefined(value)) {
3252
        value = fn(this[i], arg1, arg2, arg3);
3253
        if (isDefined(value)) {
3254
          // any function which returns a value needs to be wrapped
3255
          value = jqLite(value);
3256
        }
3257
      } else {
3258
        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3259
      }
3260
    }
3261
    return isDefined(value) ? value : this;
3262
  };
3263
 
3264
  // bind legacy bind/unbind to on/off
3265
  JQLite.prototype.bind = JQLite.prototype.on;
3266
  JQLite.prototype.unbind = JQLite.prototype.off;
3267
});
3268
 
3269
 
3270
// Provider for private $$jqLite service
3271
function $$jqLiteProvider() {
3272
  this.$get = function $$jqLite() {
3273
    return extend(JQLite, {
3274
      hasClass: function(node, classes) {
3275
        if (node.attr) node = node[0];
3276
        return jqLiteHasClass(node, classes);
3277
      },
3278
      addClass: function(node, classes) {
3279
        if (node.attr) node = node[0];
3280
        return jqLiteAddClass(node, classes);
3281
      },
3282
      removeClass: function(node, classes) {
3283
        if (node.attr) node = node[0];
3284
        return jqLiteRemoveClass(node, classes);
3285
      }
3286
    });
3287
  };
3288
}
3289
 
3290
/**
3291
 * Computes a hash of an 'obj'.
3292
 * Hash of a:
3293
 *  string is string
3294
 *  number is number as string
3295
 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3296
 *         that is also assigned to the $$hashKey property of the object.
3297
 *
3298
 * @param obj
3299
 * @returns {string} hash string such that the same input will have the same hash string.
3300
 *         The resulting string key is in 'type:hashKey' format.
3301
 */
3302
function hashKey(obj, nextUidFn) {
3303
  var key = obj && obj.$$hashKey;
3304
 
3305
  if (key) {
3306
    if (typeof key === 'function') {
3307
      key = obj.$$hashKey();
3308
    }
3309
    return key;
3310
  }
3311
 
3312
  var objType = typeof obj;
3313
  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3314
    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3315
  } else {
3316
    key = objType + ':' + obj;
3317
  }
3318
 
3319
  return key;
3320
}
3321
 
3322
/**
3323
 * HashMap which can use objects as keys
3324
 */
3325
function HashMap(array, isolatedUid) {
3326
  if (isolatedUid) {
3327
    var uid = 0;
3328
    this.nextUid = function() {
3329
      return ++uid;
3330
    };
3331
  }
3332
  forEach(array, this.put, this);
3333
}
3334
HashMap.prototype = {
3335
  /**
3336
   * Store key value pair
3337
   * @param key key to store can be any type
3338
   * @param value value to store can be any type
3339
   */
3340
  put: function(key, value) {
3341
    this[hashKey(key, this.nextUid)] = value;
3342
  },
3343
 
3344
  /**
3345
   * @param key
3346
   * @returns {Object} the value for the key
3347
   */
3348
  get: function(key) {
3349
    return this[hashKey(key, this.nextUid)];
3350
  },
3351
 
3352
  /**
3353
   * Remove the key/value pair
3354
   * @param key
3355
   */
3356
  remove: function(key) {
3357
    var value = this[key = hashKey(key, this.nextUid)];
3358
    delete this[key];
3359
    return value;
3360
  }
3361
};
3362
 
3363
/**
3364
 * @ngdoc function
3365
 * @module ng
3366
 * @name angular.injector
3367
 * @kind function
3368
 *
3369
 * @description
3370
 * Creates an injector object that can be used for retrieving services as well as for
3371
 * dependency injection (see {@link guide/di dependency injection}).
3372
 *
3373
 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3374
 *     {@link angular.module}. The `ng` module must be explicitly added.
3375
 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3376
 *     disallows argument name annotation inference.
3377
 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3378
 *
3379
 * @example
3380
 * Typical usage
3381
 * ```js
3382
 *   // create an injector
3383
 *   var $injector = angular.injector(['ng']);
3384
 *
3385
 *   // use the injector to kick off your application
3386
 *   // use the type inference to auto inject arguments, or use implicit injection
3387
 *   $injector.invoke(function($rootScope, $compile, $document) {
3388
 *     $compile($document)($rootScope);
3389
 *     $rootScope.$digest();
3390
 *   });
3391
 * ```
3392
 *
3393
 * Sometimes you want to get access to the injector of a currently running Angular app
3394
 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3395
 * application has been bootstrapped. You can do this using the extra `injector()` added
3396
 * to JQuery/jqLite elements. See {@link angular.element}.
3397
 *
3398
 * *This is fairly rare but could be the case if a third party library is injecting the
3399
 * markup.*
3400
 *
3401
 * In the following example a new block of HTML containing a `ng-controller`
3402
 * directive is added to the end of the document body by JQuery. We then compile and link
3403
 * it into the current AngularJS scope.
3404
 *
3405
 * ```js
3406
 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3407
 * $(document.body).append($div);
3408
 *
3409
 * angular.element(document).injector().invoke(function($compile) {
3410
 *   var scope = angular.element($div).scope();
3411
 *   $compile($div)(scope);
3412
 * });
3413
 * ```
3414
 */
3415
 
3416
 
3417
/**
3418
 * @ngdoc module
3419
 * @name auto
3420
 * @description
3421
 *
3422
 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3423
 */
3424
 
3425
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3426
var FN_ARG_SPLIT = /,/;
3427
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3428
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3429
var $injectorMinErr = minErr('$injector');
3430
 
3431
function anonFn(fn) {
3432
  // For anonymous functions, showing at the very least the function signature can help in
3433
  // debugging.
3434
  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3435
      args = fnText.match(FN_ARGS);
3436
  if (args) {
3437
    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3438
  }
3439
  return 'fn';
3440
}
3441
 
3442
function annotate(fn, strictDi, name) {
3443
  var $inject,
3444
      fnText,
3445
      argDecl,
3446
      last;
3447
 
3448
  if (typeof fn === 'function') {
3449
    if (!($inject = fn.$inject)) {
3450
      $inject = [];
3451
      if (fn.length) {
3452
        if (strictDi) {
3453
          if (!isString(name) || !name) {
3454
            name = fn.name || anonFn(fn);
3455
          }
3456
          throw $injectorMinErr('strictdi',
3457
            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3458
        }
3459
        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3460
        argDecl = fnText.match(FN_ARGS);
3461
        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3462
          arg.replace(FN_ARG, function(all, underscore, name) {
3463
            $inject.push(name);
3464
          });
3465
        });
3466
      }
3467
      fn.$inject = $inject;
3468
    }
3469
  } else if (isArray(fn)) {
3470
    last = fn.length - 1;
3471
    assertArgFn(fn[last], 'fn');
3472
    $inject = fn.slice(0, last);
3473
  } else {
3474
    assertArgFn(fn, 'fn', true);
3475
  }
3476
  return $inject;
3477
}
3478
 
3479
///////////////////////////////////////
3480
 
3481
/**
3482
 * @ngdoc service
3483
 * @name $injector
3484
 *
3485
 * @description
3486
 *
3487
 * `$injector` is used to retrieve object instances as defined by
3488
 * {@link auto.$provide provider}, instantiate types, invoke methods,
3489
 * and load modules.
3490
 *
3491
 * The following always holds true:
3492
 *
3493
 * ```js
3494
 *   var $injector = angular.injector();
3495
 *   expect($injector.get('$injector')).toBe($injector);
3496
 *   expect($injector.invoke(function($injector) {
3497
 *     return $injector;
3498
 *   })).toBe($injector);
3499
 * ```
3500
 *
3501
 * # Injection Function Annotation
3502
 *
3503
 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3504
 * following are all valid ways of annotating function with injection arguments and are equivalent.
3505
 *
3506
 * ```js
3507
 *   // inferred (only works if code not minified/obfuscated)
3508
 *   $injector.invoke(function(serviceA){});
3509
 *
3510
 *   // annotated
3511
 *   function explicit(serviceA) {};
3512
 *   explicit.$inject = ['serviceA'];
3513
 *   $injector.invoke(explicit);
3514
 *
3515
 *   // inline
3516
 *   $injector.invoke(['serviceA', function(serviceA){}]);
3517
 * ```
3518
 *
3519
 * ## Inference
3520
 *
3521
 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3522
 * can then be parsed and the function arguments can be extracted. This method of discovering
3523
 * annotations is disallowed when the injector is in strict mode.
3524
 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3525
 * argument names.
3526
 *
3527
 * ## `$inject` Annotation
3528
 * By adding an `$inject` property onto a function the injection parameters can be specified.
3529
 *
3530
 * ## Inline
3531
 * As an array of injection names, where the last item in the array is the function to call.
3532
 */
3533
 
3534
/**
3535
 * @ngdoc method
3536
 * @name $injector#get
3537
 *
3538
 * @description
3539
 * Return an instance of the service.
3540
 *
3541
 * @param {string} name The name of the instance to retrieve.
3542
 * @param {string} caller An optional string to provide the origin of the function call for error messages.
3543
 * @return {*} The instance.
3544
 */
3545
 
3546
/**
3547
 * @ngdoc method
3548
 * @name $injector#invoke
3549
 *
3550
 * @description
3551
 * Invoke the method and supply the method arguments from the `$injector`.
3552
 *
3553
 * @param {!Function} fn The function to invoke. Function parameters are injected according to the
3554
 *   {@link guide/di $inject Annotation} rules.
3555
 * @param {Object=} self The `this` for the invoked method.
3556
 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3557
 *                         object first, before the `$injector` is consulted.
3558
 * @returns {*} the value returned by the invoked `fn` function.
3559
 */
3560
 
3561
/**
3562
 * @ngdoc method
3563
 * @name $injector#has
3564
 *
3565
 * @description
3566
 * Allows the user to query if the particular service exists.
3567
 *
3568
 * @param {string} name Name of the service to query.
3569
 * @returns {boolean} `true` if injector has given service.
3570
 */
3571
 
3572
/**
3573
 * @ngdoc method
3574
 * @name $injector#instantiate
3575
 * @description
3576
 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3577
 * operator, and supplies all of the arguments to the constructor function as specified by the
3578
 * constructor annotation.
3579
 *
3580
 * @param {Function} Type Annotated constructor function.
3581
 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3582
 * object first, before the `$injector` is consulted.
3583
 * @returns {Object} new instance of `Type`.
3584
 */
3585
 
3586
/**
3587
 * @ngdoc method
3588
 * @name $injector#annotate
3589
 *
3590
 * @description
3591
 * Returns an array of service names which the function is requesting for injection. This API is
3592
 * used by the injector to determine which services need to be injected into the function when the
3593
 * function is invoked. There are three ways in which the function can be annotated with the needed
3594
 * dependencies.
3595
 *
3596
 * # Argument names
3597
 *
3598
 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3599
 * by converting the function into a string using `toString()` method and extracting the argument
3600
 * names.
3601
 * ```js
3602
 *   // Given
3603
 *   function MyController($scope, $route) {
3604
 *     // ...
3605
 *   }
3606
 *
3607
 *   // Then
3608
 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3609
 * ```
3610
 *
3611
 * You can disallow this method by using strict injection mode.
3612
 *
3613
 * This method does not work with code minification / obfuscation. For this reason the following
3614
 * annotation strategies are supported.
3615
 *
3616
 * # The `$inject` property
3617
 *
3618
 * If a function has an `$inject` property and its value is an array of strings, then the strings
3619
 * represent names of services to be injected into the function.
3620
 * ```js
3621
 *   // Given
3622
 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3623
 *     // ...
3624
 *   }
3625
 *   // Define function dependencies
3626
 *   MyController['$inject'] = ['$scope', '$route'];
3627
 *
3628
 *   // Then
3629
 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3630
 * ```
3631
 *
3632
 * # The array notation
3633
 *
3634
 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3635
 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3636
 * a way that survives minification is a better choice:
3637
 *
3638
 * ```js
3639
 *   // We wish to write this (not minification / obfuscation safe)
3640
 *   injector.invoke(function($compile, $rootScope) {
3641
 *     // ...
3642
 *   });
3643
 *
3644
 *   // We are forced to write break inlining
3645
 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3646
 *     // ...
3647
 *   };
3648
 *   tmpFn.$inject = ['$compile', '$rootScope'];
3649
 *   injector.invoke(tmpFn);
3650
 *
3651
 *   // To better support inline function the inline annotation is supported
3652
 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3653
 *     // ...
3654
 *   }]);
3655
 *
3656
 *   // Therefore
3657
 *   expect(injector.annotate(
3658
 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3659
 *    ).toEqual(['$compile', '$rootScope']);
3660
 * ```
3661
 *
3662
 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3663
 * be retrieved as described above.
3664
 *
3665
 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3666
 *
3667
 * @returns {Array.<string>} The names of the services which the function requires.
3668
 */
3669
 
3670
 
3671
 
3672
 
3673
/**
3674
 * @ngdoc service
3675
 * @name $provide
3676
 *
3677
 * @description
3678
 *
3679
 * The {@link auto.$provide $provide} service has a number of methods for registering components
3680
 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3681
 * {@link angular.Module}.
3682
 *
3683
 * An Angular **service** is a singleton object created by a **service factory**.  These **service
3684
 * factories** are functions which, in turn, are created by a **service provider**.
3685
 * The **service providers** are constructor functions. When instantiated they must contain a
3686
 * property called `$get`, which holds the **service factory** function.
3687
 *
3688
 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3689
 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3690
 * function to get the instance of the **service**.
3691
 *
3692
 * Often services have no configuration options and there is no need to add methods to the service
3693
 * provider.  The provider will be no more than a constructor function with a `$get` property. For
3694
 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3695
 * services without specifying a provider.
3696
 *
3697
 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3698
 *     {@link auto.$injector $injector}
3699
 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3700
 *     providers and services.
3701
 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3702
 *     services, not providers.
3703
 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3704
 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
3705
 *     given factory function.
3706
 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3707
 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3708
 *      a new object using the given constructor function.
3709
 *
3710
 * See the individual methods for more information and examples.
3711
 */
3712
 
3713
/**
3714
 * @ngdoc method
3715
 * @name $provide#provider
3716
 * @description
3717
 *
3718
 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3719
 * are constructor functions, whose instances are responsible for "providing" a factory for a
3720
 * service.
3721
 *
3722
 * Service provider names start with the name of the service they provide followed by `Provider`.
3723
 * For example, the {@link ng.$log $log} service has a provider called
3724
 * {@link ng.$logProvider $logProvider}.
3725
 *
3726
 * Service provider objects can have additional methods which allow configuration of the provider
3727
 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3728
 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3729
 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3730
 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3731
 * console or not.
3732
 *
3733
 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3734
                        'Provider'` key.
3735
 * @param {(Object|function())} provider If the provider is:
3736
 *
3737
 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3738
 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3739
 *   - `Constructor`: a new instance of the provider will be created using
3740
 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3741
 *
3742
 * @returns {Object} registered provider instance
3743
 
3744
 * @example
3745
 *
3746
 * The following example shows how to create a simple event tracking service and register it using
3747
 * {@link auto.$provide#provider $provide.provider()}.
3748
 *
3749
 * ```js
3750
 *  // Define the eventTracker provider
3751
 *  function EventTrackerProvider() {
3752
 *    var trackingUrl = '/track';
3753
 *
3754
 *    // A provider method for configuring where the tracked events should been saved
3755
 *    this.setTrackingUrl = function(url) {
3756
 *      trackingUrl = url;
3757
 *    };
3758
 *
3759
 *    // The service factory function
3760
 *    this.$get = ['$http', function($http) {
3761
 *      var trackedEvents = {};
3762
 *      return {
3763
 *        // Call this to track an event
3764
 *        event: function(event) {
3765
 *          var count = trackedEvents[event] || 0;
3766
 *          count += 1;
3767
 *          trackedEvents[event] = count;
3768
 *          return count;
3769
 *        },
3770
 *        // Call this to save the tracked events to the trackingUrl
3771
 *        save: function() {
3772
 *          $http.post(trackingUrl, trackedEvents);
3773
 *        }
3774
 *      };
3775
 *    }];
3776
 *  }
3777
 *
3778
 *  describe('eventTracker', function() {
3779
 *    var postSpy;
3780
 *
3781
 *    beforeEach(module(function($provide) {
3782
 *      // Register the eventTracker provider
3783
 *      $provide.provider('eventTracker', EventTrackerProvider);
3784
 *    }));
3785
 *
3786
 *    beforeEach(module(function(eventTrackerProvider) {
3787
 *      // Configure eventTracker provider
3788
 *      eventTrackerProvider.setTrackingUrl('/custom-track');
3789
 *    }));
3790
 *
3791
 *    it('tracks events', inject(function(eventTracker) {
3792
 *      expect(eventTracker.event('login')).toEqual(1);
3793
 *      expect(eventTracker.event('login')).toEqual(2);
3794
 *    }));
3795
 *
3796
 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
3797
 *      postSpy = spyOn($http, 'post');
3798
 *      eventTracker.event('login');
3799
 *      eventTracker.save();
3800
 *      expect(postSpy).toHaveBeenCalled();
3801
 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3802
 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3803
 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3804
 *    }));
3805
 *  });
3806
 * ```
3807
 */
3808
 
3809
/**
3810
 * @ngdoc method
3811
 * @name $provide#factory
3812
 * @description
3813
 *
3814
 * Register a **service factory**, which will be called to return the service instance.
3815
 * This is short for registering a service where its provider consists of only a `$get` property,
3816
 * which is the given service factory function.
3817
 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3818
 * configure your service in a provider.
3819
 *
3820
 * @param {string} name The name of the instance.
3821
 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
3822
 *                            for `$provide.provider(name, {$get: $getFn})`.
3823
 * @returns {Object} registered provider instance
3824
 *
3825
 * @example
3826
 * Here is an example of registering a service
3827
 * ```js
3828
 *   $provide.factory('ping', ['$http', function($http) {
3829
 *     return function ping() {
3830
 *       return $http.send('/ping');
3831
 *     };
3832
 *   }]);
3833
 * ```
3834
 * You would then inject and use this service like this:
3835
 * ```js
3836
 *   someModule.controller('Ctrl', ['ping', function(ping) {
3837
 *     ping();
3838
 *   }]);
3839
 * ```
3840
 */
3841
 
3842
 
3843
/**
3844
 * @ngdoc method
3845
 * @name $provide#service
3846
 * @description
3847
 *
3848
 * Register a **service constructor**, which will be invoked with `new` to create the service
3849
 * instance.
3850
 * This is short for registering a service where its provider's `$get` property is the service
3851
 * constructor function that will be used to instantiate the service instance.
3852
 *
3853
 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3854
 * as a type/class.
3855
 *
3856
 * @param {string} name The name of the instance.
3857
 * @param {Function} constructor A class (constructor function) that will be instantiated.
3858
 * @returns {Object} registered provider instance
3859
 *
3860
 * @example
3861
 * Here is an example of registering a service using
3862
 * {@link auto.$provide#service $provide.service(class)}.
3863
 * ```js
3864
 *   var Ping = function($http) {
3865
 *     this.$http = $http;
3866
 *   };
3867
 *
3868
 *   Ping.$inject = ['$http'];
3869
 *
3870
 *   Ping.prototype.send = function() {
3871
 *     return this.$http.get('/ping');
3872
 *   };
3873
 *   $provide.service('ping', Ping);
3874
 * ```
3875
 * You would then inject and use this service like this:
3876
 * ```js
3877
 *   someModule.controller('Ctrl', ['ping', function(ping) {
3878
 *     ping.send();
3879
 *   }]);
3880
 * ```
3881
 */
3882
 
3883
 
3884
/**
3885
 * @ngdoc method
3886
 * @name $provide#value
3887
 * @description
3888
 *
3889
 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3890
 * number, an array, an object or a function.  This is short for registering a service where its
3891
 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3892
 * service**.
3893
 *
3894
 * Value services are similar to constant services, except that they cannot be injected into a
3895
 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3896
 * an Angular
3897
 * {@link auto.$provide#decorator decorator}.
3898
 *
3899
 * @param {string} name The name of the instance.
3900
 * @param {*} value The value.
3901
 * @returns {Object} registered provider instance
3902
 *
3903
 * @example
3904
 * Here are some examples of creating value services.
3905
 * ```js
3906
 *   $provide.value('ADMIN_USER', 'admin');
3907
 *
3908
 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3909
 *
3910
 *   $provide.value('halfOf', function(value) {
3911
 *     return value / 2;
3912
 *   });
3913
 * ```
3914
 */
3915
 
3916
 
3917
/**
3918
 * @ngdoc method
3919
 * @name $provide#constant
3920
 * @description
3921
 *
3922
 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3923
 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3924
 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3925
 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3926
 *
3927
 * @param {string} name The name of the constant.
3928
 * @param {*} value The constant value.
3929
 * @returns {Object} registered instance
3930
 *
3931
 * @example
3932
 * Here a some examples of creating constants:
3933
 * ```js
3934
 *   $provide.constant('SHARD_HEIGHT', 306);
3935
 *
3936
 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3937
 *
3938
 *   $provide.constant('double', function(value) {
3939
 *     return value * 2;
3940
 *   });
3941
 * ```
3942
 */
3943
 
3944
 
3945
/**
3946
 * @ngdoc method
3947
 * @name $provide#decorator
3948
 * @description
3949
 *
3950
 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3951
 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3952
 * service. The object returned by the decorator may be the original service, or a new service
3953
 * object which replaces or wraps and delegates to the original service.
3954
 *
3955
 * @param {string} name The name of the service to decorate.
3956
 * @param {function()} decorator This function will be invoked when the service needs to be
3957
 *    instantiated and should return the decorated service instance. The function is called using
3958
 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3959
 *    Local injection arguments:
3960
 *
3961
 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
3962
 *      decorated or delegated to.
3963
 *
3964
 * @example
3965
 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
3966
 * calls to {@link ng.$log#error $log.warn()}.
3967
 * ```js
3968
 *   $provide.decorator('$log', ['$delegate', function($delegate) {
3969
 *     $delegate.warn = $delegate.error;
3970
 *     return $delegate;
3971
 *   }]);
3972
 * ```
3973
 */
3974
 
3975
 
3976
function createInjector(modulesToLoad, strictDi) {
3977
  strictDi = (strictDi === true);
3978
  var INSTANTIATING = {},
3979
      providerSuffix = 'Provider',
3980
      path = [],
3981
      loadedModules = new HashMap([], true),
3982
      providerCache = {
3983
        $provide: {
3984
            provider: supportObject(provider),
3985
            factory: supportObject(factory),
3986
            service: supportObject(service),
3987
            value: supportObject(value),
3988
            constant: supportObject(constant),
3989
            decorator: decorator
3990
          }
3991
      },
3992
      providerInjector = (providerCache.$injector =
3993
          createInternalInjector(providerCache, function(serviceName, caller) {
3994
            if (angular.isString(caller)) {
3995
              path.push(caller);
3996
            }
3997
            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
3998
          })),
3999
      instanceCache = {},
4000
      instanceInjector = (instanceCache.$injector =
4001
          createInternalInjector(instanceCache, function(serviceName, caller) {
4002
            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4003
            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4004
          }));
4005
 
4006
 
4007
  forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
4008
 
4009
  return instanceInjector;
4010
 
4011
  ////////////////////////////////////
4012
  // $provider
4013
  ////////////////////////////////////
4014
 
4015
  function supportObject(delegate) {
4016
    return function(key, value) {
4017
      if (isObject(key)) {
4018
        forEach(key, reverseParams(delegate));
4019
      } else {
4020
        return delegate(key, value);
4021
      }
4022
    };
4023
  }
4024
 
4025
  function provider(name, provider_) {
4026
    assertNotHasOwnProperty(name, 'service');
4027
    if (isFunction(provider_) || isArray(provider_)) {
4028
      provider_ = providerInjector.instantiate(provider_);
4029
    }
4030
    if (!provider_.$get) {
4031
      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4032
    }
4033
    return providerCache[name + providerSuffix] = provider_;
4034
  }
4035
 
4036
  function enforceReturnValue(name, factory) {
4037
    return function enforcedReturnValue() {
4038
      var result = instanceInjector.invoke(factory, this);
4039
      if (isUndefined(result)) {
4040
        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4041
      }
4042
      return result;
4043
    };
4044
  }
4045
 
4046
  function factory(name, factoryFn, enforce) {
4047
    return provider(name, {
4048
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4049
    });
4050
  }
4051
 
4052
  function service(name, constructor) {
4053
    return factory(name, ['$injector', function($injector) {
4054
      return $injector.instantiate(constructor);
4055
    }]);
4056
  }
4057
 
4058
  function value(name, val) { return factory(name, valueFn(val), false); }
4059
 
4060
  function constant(name, value) {
4061
    assertNotHasOwnProperty(name, 'constant');
4062
    providerCache[name] = value;
4063
    instanceCache[name] = value;
4064
  }
4065
 
4066
  function decorator(serviceName, decorFn) {
4067
    var origProvider = providerInjector.get(serviceName + providerSuffix),
4068
        orig$get = origProvider.$get;
4069
 
4070
    origProvider.$get = function() {
4071
      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4072
      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4073
    };
4074
  }
4075
 
4076
  ////////////////////////////////////
4077
  // Module Loading
4078
  ////////////////////////////////////
4079
  function loadModules(modulesToLoad) {
4080
    var runBlocks = [], moduleFn;
4081
    forEach(modulesToLoad, function(module) {
4082
      if (loadedModules.get(module)) return;
4083
      loadedModules.put(module, true);
4084
 
4085
      function runInvokeQueue(queue) {
4086
        var i, ii;
4087
        for (i = 0, ii = queue.length; i < ii; i++) {
4088
          var invokeArgs = queue[i],
4089
              provider = providerInjector.get(invokeArgs[0]);
4090
 
4091
          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4092
        }
4093
      }
4094
 
4095
      try {
4096
        if (isString(module)) {
4097
          moduleFn = angularModule(module);
4098
          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4099
          runInvokeQueue(moduleFn._invokeQueue);
4100
          runInvokeQueue(moduleFn._configBlocks);
4101
        } else if (isFunction(module)) {
4102
            runBlocks.push(providerInjector.invoke(module));
4103
        } else if (isArray(module)) {
4104
            runBlocks.push(providerInjector.invoke(module));
4105
        } else {
4106
          assertArgFn(module, 'module');
4107
        }
4108
      } catch (e) {
4109
        if (isArray(module)) {
4110
          module = module[module.length - 1];
4111
        }
4112
        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4113
          // Safari & FF's stack traces don't contain error.message content
4114
          // unlike those of Chrome and IE
4115
          // So if stack doesn't contain message, we create a new string that contains both.
4116
          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4117
          /* jshint -W022 */
4118
          e = e.message + '\n' + e.stack;
4119
        }
4120
        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4121
                  module, e.stack || e.message || e);
4122
      }
4123
    });
4124
    return runBlocks;
4125
  }
4126
 
4127
  ////////////////////////////////////
4128
  // internal Injector
4129
  ////////////////////////////////////
4130
 
4131
  function createInternalInjector(cache, factory) {
4132
 
4133
    function getService(serviceName, caller) {
4134
      if (cache.hasOwnProperty(serviceName)) {
4135
        if (cache[serviceName] === INSTANTIATING) {
4136
          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4137
                    serviceName + ' <- ' + path.join(' <- '));
4138
        }
4139
        return cache[serviceName];
4140
      } else {
4141
        try {
4142
          path.unshift(serviceName);
4143
          cache[serviceName] = INSTANTIATING;
4144
          return cache[serviceName] = factory(serviceName, caller);
4145
        } catch (err) {
4146
          if (cache[serviceName] === INSTANTIATING) {
4147
            delete cache[serviceName];
4148
          }
4149
          throw err;
4150
        } finally {
4151
          path.shift();
4152
        }
4153
      }
4154
    }
4155
 
4156
    function invoke(fn, self, locals, serviceName) {
4157
      if (typeof locals === 'string') {
4158
        serviceName = locals;
4159
        locals = null;
4160
      }
4161
 
4162
      var args = [],
4163
          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4164
          length, i,
4165
          key;
4166
 
4167
      for (i = 0, length = $inject.length; i < length; i++) {
4168
        key = $inject[i];
4169
        if (typeof key !== 'string') {
4170
          throw $injectorMinErr('itkn',
4171
                  'Incorrect injection token! Expected service name as string, got {0}', key);
4172
        }
4173
        args.push(
4174
          locals && locals.hasOwnProperty(key)
4175
          ? locals[key]
4176
          : getService(key, serviceName)
4177
        );
4178
      }
4179
      if (isArray(fn)) {
4180
        fn = fn[length];
4181
      }
4182
 
4183
      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4184
      // #5388
4185
      return fn.apply(self, args);
4186
    }
4187
 
4188
    function instantiate(Type, locals, serviceName) {
4189
      // Check if Type is annotated and use just the given function at n-1 as parameter
4190
      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4191
      // Object creation: http://jsperf.com/create-constructor/2
4192
      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4193
      var returnedValue = invoke(Type, instance, locals, serviceName);
4194
 
4195
      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4196
    }
4197
 
4198
    return {
4199
      invoke: invoke,
4200
      instantiate: instantiate,
4201
      get: getService,
4202
      annotate: createInjector.$$annotate,
4203
      has: function(name) {
4204
        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4205
      }
4206
    };
4207
  }
4208
}
4209
 
4210
createInjector.$$annotate = annotate;
4211
 
4212
/**
4213
 * @ngdoc provider
4214
 * @name $anchorScrollProvider
4215
 *
4216
 * @description
4217
 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4218
 * {@link ng.$location#hash $location.hash()} changes.
4219
 */
4220
function $AnchorScrollProvider() {
4221
 
4222
  var autoScrollingEnabled = true;
4223
 
4224
  /**
4225
   * @ngdoc method
4226
   * @name $anchorScrollProvider#disableAutoScrolling
4227
   *
4228
   * @description
4229
   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4230
   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4231
   * Use this method to disable automatic scrolling.
4232
   *
4233
   * If automatic scrolling is disabled, one must explicitly call
4234
   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4235
   * current hash.
4236
   */
4237
  this.disableAutoScrolling = function() {
4238
    autoScrollingEnabled = false;
4239
  };
4240
 
4241
  /**
4242
   * @ngdoc service
4243
   * @name $anchorScroll
4244
   * @kind function
4245
   * @requires $window
4246
   * @requires $location
4247
   * @requires $rootScope
4248
   *
4249
   * @description
4250
   * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
4251
   * scrolls to the related element, according to the rules specified in the
4252
   * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4253
   *
4254
   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4255
   * match any anchor whenever it changes. This can be disabled by calling
4256
   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4257
   *
4258
   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4259
   * vertical scroll-offset (either fixed or dynamic).
4260
   *
4261
   * @property {(number|function|jqLite)} yOffset
4262
   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4263
   * positioned elements at the top of the page, such as navbars, headers etc.
4264
   *
4265
   * `yOffset` can be specified in various ways:
4266
   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4267
   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4268
   *   a number representing the offset (in pixels).<br /><br />
4269
   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4270
   *   the top of the page to the element's bottom will be used as offset.<br />
4271
   *   **Note**: The element will be taken into account only as long as its `position` is set to
4272
   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4273
   *   their height and/or positioning according to the viewport's size.
4274
   *
4275
   * <br />
4276
   * <div class="alert alert-warning">
4277
   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4278
   * not some child element.
4279
   * </div>
4280
   *
4281
   * @example
4282
     <example module="anchorScrollExample">
4283
       <file name="index.html">
4284
         <div id="scrollArea" ng-controller="ScrollController">
4285
           <a ng-click="gotoBottom()">Go to bottom</a>
4286
           <a id="bottom"></a> You're at the bottom!
4287
         </div>
4288
       </file>
4289
       <file name="script.js">
4290
         angular.module('anchorScrollExample', [])
4291
           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4292
             function ($scope, $location, $anchorScroll) {
4293
               $scope.gotoBottom = function() {
4294
                 // set the location.hash to the id of
4295
                 // the element you wish to scroll to.
4296
                 $location.hash('bottom');
4297
 
4298
                 // call $anchorScroll()
4299
                 $anchorScroll();
4300
               };
4301
             }]);
4302
       </file>
4303
       <file name="style.css">
4304
         #scrollArea {
4305
           height: 280px;
4306
           overflow: auto;
4307
         }
4308
 
4309
         #bottom {
4310
           display: block;
4311
           margin-top: 2000px;
4312
         }
4313
       </file>
4314
     </example>
4315
   *
4316
   * <hr />
4317
   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4318
   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4319
   *
4320
   * @example
4321
     <example module="anchorScrollOffsetExample">
4322
       <file name="index.html">
4323
         <div class="fixed-header" ng-controller="headerCtrl">
4324
           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4325
             Go to anchor {{x}}
4326
           </a>
4327
         </div>
4328
         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4329
           Anchor {{x}} of 5
4330
         </div>
4331
       </file>
4332
       <file name="script.js">
4333
         angular.module('anchorScrollOffsetExample', [])
4334
           .run(['$anchorScroll', function($anchorScroll) {
4335
             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4336
           }])
4337
           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4338
             function ($anchorScroll, $location, $scope) {
4339
               $scope.gotoAnchor = function(x) {
4340
                 var newHash = 'anchor' + x;
4341
                 if ($location.hash() !== newHash) {
4342
                   // set the $location.hash to `newHash` and
4343
                   // $anchorScroll will automatically scroll to it
4344
                   $location.hash('anchor' + x);
4345
                 } else {
4346
                   // call $anchorScroll() explicitly,
4347
                   // since $location.hash hasn't changed
4348
                   $anchorScroll();
4349
                 }
4350
               };
4351
             }
4352
           ]);
4353
       </file>
4354
       <file name="style.css">
4355
         body {
4356
           padding-top: 50px;
4357
         }
4358
 
4359
         .anchor {
4360
           border: 2px dashed DarkOrchid;
4361
           padding: 10px 10px 200px 10px;
4362
         }
4363
 
4364
         .fixed-header {
4365
           background-color: rgba(0, 0, 0, 0.2);
4366
           height: 50px;
4367
           position: fixed;
4368
           top: 0; left: 0; right: 0;
4369
         }
4370
 
4371
         .fixed-header > a {
4372
           display: inline-block;
4373
           margin: 5px 15px;
4374
         }
4375
       </file>
4376
     </example>
4377
   */
4378
  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4379
    var document = $window.document;
4380
 
4381
    // Helper function to get first anchor from a NodeList
4382
    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4383
    //  and working in all supported browsers.)
4384
    function getFirstAnchor(list) {
4385
      var result = null;
4386
      Array.prototype.some.call(list, function(element) {
4387
        if (nodeName_(element) === 'a') {
4388
          result = element;
4389
          return true;
4390
        }
4391
      });
4392
      return result;
4393
    }
4394
 
4395
    function getYOffset() {
4396
 
4397
      var offset = scroll.yOffset;
4398
 
4399
      if (isFunction(offset)) {
4400
        offset = offset();
4401
      } else if (isElement(offset)) {
4402
        var elem = offset[0];
4403
        var style = $window.getComputedStyle(elem);
4404
        if (style.position !== 'fixed') {
4405
          offset = 0;
4406
        } else {
4407
          offset = elem.getBoundingClientRect().bottom;
4408
        }
4409
      } else if (!isNumber(offset)) {
4410
        offset = 0;
4411
      }
4412
 
4413
      return offset;
4414
    }
4415
 
4416
    function scrollTo(elem) {
4417
      if (elem) {
4418
        elem.scrollIntoView();
4419
 
4420
        var offset = getYOffset();
4421
 
4422
        if (offset) {
4423
          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4424
          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4425
          // top of the viewport.
4426
          //
4427
          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4428
          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4429
          // way down the page.
4430
          //
4431
          // This is often the case for elements near the bottom of the page.
4432
          //
4433
          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4434
          // the top of the element and the offset, which is enough to align the top of `elem` at the
4435
          // desired position.
4436
          var elemTop = elem.getBoundingClientRect().top;
4437
          $window.scrollBy(0, elemTop - offset);
4438
        }
4439
      } else {
4440
        $window.scrollTo(0, 0);
4441
      }
4442
    }
4443
 
4444
    function scroll() {
4445
      var hash = $location.hash(), elm;
4446
 
4447
      // empty hash, scroll to the top of the page
4448
      if (!hash) scrollTo(null);
4449
 
4450
      // element with given id
4451
      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4452
 
4453
      // first anchor with given name :-D
4454
      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4455
 
4456
      // no element and hash == 'top', scroll to the top of the page
4457
      else if (hash === 'top') scrollTo(null);
4458
    }
4459
 
4460
    // does not scroll when user clicks on anchor link that is currently on
4461
    // (no url change, no $location.hash() change), browser native does scroll
4462
    if (autoScrollingEnabled) {
4463
      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4464
        function autoScrollWatchAction(newVal, oldVal) {
4465
          // skip the initial scroll if $location.hash is empty
4466
          if (newVal === oldVal && newVal === '') return;
4467
 
4468
          jqLiteDocumentLoaded(function() {
4469
            $rootScope.$evalAsync(scroll);
4470
          });
4471
        });
4472
    }
4473
 
4474
    return scroll;
4475
  }];
4476
}
4477
 
4478
var $animateMinErr = minErr('$animate');
4479
 
4480
/**
4481
 * @ngdoc provider
4482
 * @name $animateProvider
4483
 *
4484
 * @description
4485
 * Default implementation of $animate that doesn't perform any animations, instead just
4486
 * synchronously performs DOM
4487
 * updates and calls done() callbacks.
4488
 *
4489
 * In order to enable animations the ngAnimate module has to be loaded.
4490
 *
4491
 * To see the functional implementation check out src/ngAnimate/animate.js
4492
 */
4493
var $AnimateProvider = ['$provide', function($provide) {
4494
 
4495
 
4496
  this.$$selectors = {};
4497
 
4498
 
4499
  /**
4500
   * @ngdoc method
4501
   * @name $animateProvider#register
4502
   *
4503
   * @description
4504
   * Registers a new injectable animation factory function. The factory function produces the
4505
   * animation object which contains callback functions for each event that is expected to be
4506
   * animated.
4507
   *
4508
   *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4509
   *   must be called once the element animation is complete. If a function is returned then the
4510
   *   animation service will use this function to cancel the animation whenever a cancel event is
4511
   *   triggered.
4512
   *
4513
   *
4514
   * ```js
4515
   *   return {
4516
     *     eventFn : function(element, done) {
4517
     *       //code to run the animation
4518
     *       //once complete, then run done()
4519
     *       return function cancellationFunction() {
4520
     *         //code to cancel the animation
4521
     *       }
4522
     *     }
4523
     *   }
4524
   * ```
4525
   *
4526
   * @param {string} name The name of the animation.
4527
   * @param {Function} factory The factory function that will be executed to return the animation
4528
   *                           object.
4529
   */
4530
  this.register = function(name, factory) {
4531
    var key = name + '-animation';
4532
    if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4533
        "Expecting class selector starting with '.' got '{0}'.", name);
4534
    this.$$selectors[name.substr(1)] = key;
4535
    $provide.factory(key, factory);
4536
  };
4537
 
4538
  /**
4539
   * @ngdoc method
4540
   * @name $animateProvider#classNameFilter
4541
   *
4542
   * @description
4543
   * Sets and/or returns the CSS class regular expression that is checked when performing
4544
   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4545
   * therefore enable $animate to attempt to perform an animation on any element.
4546
   * When setting the classNameFilter value, animations will only be performed on elements
4547
   * that successfully match the filter expression. This in turn can boost performance
4548
   * for low-powered devices as well as applications containing a lot of structural operations.
4549
   * @param {RegExp=} expression The className expression which will be checked against all animations
4550
   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4551
   */
4552
  this.classNameFilter = function(expression) {
4553
    if (arguments.length === 1) {
4554
      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4555
    }
4556
    return this.$$classNameFilter;
4557
  };
4558
 
4559
  this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
4560
 
4561
    var currentDefer;
4562
 
4563
    function runAnimationPostDigest(fn) {
4564
      var cancelFn, defer = $$q.defer();
4565
      defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
4566
        cancelFn && cancelFn();
4567
      };
4568
 
4569
      $rootScope.$$postDigest(function ngAnimatePostDigest() {
4570
        cancelFn = fn(function ngAnimateNotifyComplete() {
4571
          defer.resolve();
4572
        });
4573
      });
4574
 
4575
      return defer.promise;
4576
    }
4577
 
4578
    function resolveElementClasses(element, classes) {
4579
      var toAdd = [], toRemove = [];
4580
 
4581
      var hasClasses = createMap();
4582
      forEach((element.attr('class') || '').split(/\s+/), function(className) {
4583
        hasClasses[className] = true;
4584
      });
4585
 
4586
      forEach(classes, function(status, className) {
4587
        var hasClass = hasClasses[className];
4588
 
4589
        // If the most recent class manipulation (via $animate) was to remove the class, and the
4590
        // element currently has the class, the class is scheduled for removal. Otherwise, if
4591
        // the most recent class manipulation (via $animate) was to add the class, and the
4592
        // element does not currently have the class, the class is scheduled to be added.
4593
        if (status === false && hasClass) {
4594
          toRemove.push(className);
4595
        } else if (status === true && !hasClass) {
4596
          toAdd.push(className);
4597
        }
4598
      });
4599
 
4600
      return (toAdd.length + toRemove.length) > 0 &&
4601
        [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
4602
    }
4603
 
4604
    function cachedClassManipulation(cache, classes, op) {
4605
      for (var i=0, ii = classes.length; i < ii; ++i) {
4606
        var className = classes[i];
4607
        cache[className] = op;
4608
      }
4609
    }
4610
 
4611
    function asyncPromise() {
4612
      // only serve one instance of a promise in order to save CPU cycles
4613
      if (!currentDefer) {
4614
        currentDefer = $$q.defer();
4615
        $$asyncCallback(function() {
4616
          currentDefer.resolve();
4617
          currentDefer = null;
4618
        });
4619
      }
4620
      return currentDefer.promise;
4621
    }
4622
 
4623
    function applyStyles(element, options) {
4624
      if (angular.isObject(options)) {
4625
        var styles = extend(options.from || {}, options.to || {});
4626
        element.css(styles);
4627
      }
4628
    }
4629
 
4630
    /**
4631
     *
4632
     * @ngdoc service
4633
     * @name $animate
4634
     * @description The $animate service provides rudimentary DOM manipulation functions to
4635
     * insert, remove and move elements within the DOM, as well as adding and removing classes.
4636
     * This service is the core service used by the ngAnimate $animator service which provides
4637
     * high-level animation hooks for CSS and JavaScript.
4638
     *
4639
     * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4640
     * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4641
     * manipulation operations.
4642
     *
4643
     * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4644
     * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4645
     * page}.
4646
     */
4647
    return {
4648
      animate: function(element, from, to) {
4649
        applyStyles(element, { from: from, to: to });
4650
        return asyncPromise();
4651
      },
4652
 
4653
      /**
4654
       *
4655
       * @ngdoc method
4656
       * @name $animate#enter
4657
       * @kind function
4658
       * @description Inserts the element into the DOM either after the `after` element or
4659
       * as the first child within the `parent` element. When the function is called a promise
4660
       * is returned that will be resolved at a later time.
4661
       * @param {DOMElement} element the element which will be inserted into the DOM
4662
       * @param {DOMElement} parent the parent element which will append the element as
4663
       *   a child (if the after element is not present)
4664
       * @param {DOMElement} after the sibling element which will append the element
4665
       *   after itself
4666
       * @param {object=} options an optional collection of styles that will be applied to the element.
4667
       * @return {Promise} the animation callback promise
4668
       */
4669
      enter: function(element, parent, after, options) {
4670
        applyStyles(element, options);
4671
        after ? after.after(element)
4672
              : parent.prepend(element);
4673
        return asyncPromise();
4674
      },
4675
 
4676
      /**
4677
       *
4678
       * @ngdoc method
4679
       * @name $animate#leave
4680
       * @kind function
4681
       * @description Removes the element from the DOM. When the function is called a promise
4682
       * is returned that will be resolved at a later time.
4683
       * @param {DOMElement} element the element which will be removed from the DOM
4684
       * @param {object=} options an optional collection of options that will be applied to the element.
4685
       * @return {Promise} the animation callback promise
4686
       */
4687
      leave: function(element, options) {
4688
        element.remove();
4689
        return asyncPromise();
4690
      },
4691
 
4692
      /**
4693
       *
4694
       * @ngdoc method
4695
       * @name $animate#move
4696
       * @kind function
4697
       * @description Moves the position of the provided element within the DOM to be placed
4698
       * either after the `after` element or inside of the `parent` element. When the function
4699
       * is called a promise is returned that will be resolved at a later time.
4700
       *
4701
       * @param {DOMElement} element the element which will be moved around within the
4702
       *   DOM
4703
       * @param {DOMElement} parent the parent element where the element will be
4704
       *   inserted into (if the after element is not present)
4705
       * @param {DOMElement} after the sibling element where the element will be
4706
       *   positioned next to
4707
       * @param {object=} options an optional collection of options that will be applied to the element.
4708
       * @return {Promise} the animation callback promise
4709
       */
4710
      move: function(element, parent, after, options) {
4711
        // Do not remove element before insert. Removing will cause data associated with the
4712
        // element to be dropped. Insert will implicitly do the remove.
4713
        return this.enter(element, parent, after, options);
4714
      },
4715
 
4716
      /**
4717
       *
4718
       * @ngdoc method
4719
       * @name $animate#addClass
4720
       * @kind function
4721
       * @description Adds the provided className CSS class value to the provided element.
4722
       * When the function is called a promise is returned that will be resolved at a later time.
4723
       * @param {DOMElement} element the element which will have the className value
4724
       *   added to it
4725
       * @param {string} className the CSS class which will be added to the element
4726
       * @param {object=} options an optional collection of options that will be applied to the element.
4727
       * @return {Promise} the animation callback promise
4728
       */
4729
      addClass: function(element, className, options) {
4730
        return this.setClass(element, className, [], options);
4731
      },
4732
 
4733
      $$addClassImmediately: function(element, className, options) {
4734
        element = jqLite(element);
4735
        className = !isString(className)
4736
                        ? (isArray(className) ? className.join(' ') : '')
4737
                        : className;
4738
        forEach(element, function(element) {
4739
          jqLiteAddClass(element, className);
4740
        });
4741
        applyStyles(element, options);
4742
        return asyncPromise();
4743
      },
4744
 
4745
      /**
4746
       *
4747
       * @ngdoc method
4748
       * @name $animate#removeClass
4749
       * @kind function
4750
       * @description Removes the provided className CSS class value from the provided element.
4751
       * When the function is called a promise is returned that will be resolved at a later time.
4752
       * @param {DOMElement} element the element which will have the className value
4753
       *   removed from it
4754
       * @param {string} className the CSS class which will be removed from the element
4755
       * @param {object=} options an optional collection of options that will be applied to the element.
4756
       * @return {Promise} the animation callback promise
4757
       */
4758
      removeClass: function(element, className, options) {
4759
        return this.setClass(element, [], className, options);
4760
      },
4761
 
4762
      $$removeClassImmediately: function(element, className, options) {
4763
        element = jqLite(element);
4764
        className = !isString(className)
4765
                        ? (isArray(className) ? className.join(' ') : '')
4766
                        : className;
4767
        forEach(element, function(element) {
4768
          jqLiteRemoveClass(element, className);
4769
        });
4770
        applyStyles(element, options);
4771
        return asyncPromise();
4772
      },
4773
 
4774
      /**
4775
       *
4776
       * @ngdoc method
4777
       * @name $animate#setClass
4778
       * @kind function
4779
       * @description Adds and/or removes the given CSS classes to and from the element.
4780
       * When the function is called a promise is returned that will be resolved at a later time.
4781
       * @param {DOMElement} element the element which will have its CSS classes changed
4782
       *   removed from it
4783
       * @param {string} add the CSS classes which will be added to the element
4784
       * @param {string} remove the CSS class which will be removed from the element
4785
       * @param {object=} options an optional collection of options that will be applied to the element.
4786
       * @return {Promise} the animation callback promise
4787
       */
4788
      setClass: function(element, add, remove, options) {
4789
        var self = this;
4790
        var STORAGE_KEY = '$$animateClasses';
4791
        var createdCache = false;
4792
        element = jqLite(element);
4793
 
4794
        var cache = element.data(STORAGE_KEY);
4795
        if (!cache) {
4796
          cache = {
4797
            classes: {},
4798
            options: options
4799
          };
4800
          createdCache = true;
4801
        } else if (options && cache.options) {
4802
          cache.options = angular.extend(cache.options || {}, options);
4803
        }
4804
 
4805
        var classes = cache.classes;
4806
 
4807
        add = isArray(add) ? add : add.split(' ');
4808
        remove = isArray(remove) ? remove : remove.split(' ');
4809
        cachedClassManipulation(classes, add, true);
4810
        cachedClassManipulation(classes, remove, false);
4811
 
4812
        if (createdCache) {
4813
          cache.promise = runAnimationPostDigest(function(done) {
4814
            var cache = element.data(STORAGE_KEY);
4815
            element.removeData(STORAGE_KEY);
4816
 
4817
            // in the event that the element is removed before postDigest
4818
            // is run then the cache will be undefined and there will be
4819
            // no need anymore to add or remove and of the element classes
4820
            if (cache) {
4821
              var classes = resolveElementClasses(element, cache.classes);
4822
              if (classes) {
4823
                self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
4824
              }
4825
            }
4826
 
4827
            done();
4828
          });
4829
          element.data(STORAGE_KEY, cache);
4830
        }
4831
 
4832
        return cache.promise;
4833
      },
4834
 
4835
      $$setClassImmediately: function(element, add, remove, options) {
4836
        add && this.$$addClassImmediately(element, add);
4837
        remove && this.$$removeClassImmediately(element, remove);
4838
        applyStyles(element, options);
4839
        return asyncPromise();
4840
      },
4841
 
4842
      enabled: noop,
4843
      cancel: noop
4844
    };
4845
  }];
4846
}];
4847
 
4848
function $$AsyncCallbackProvider() {
4849
  this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4850
    return $$rAF.supported
4851
      ? function(fn) { return $$rAF(fn); }
4852
      : function(fn) {
4853
        return $timeout(fn, 0, false);
4854
      };
4855
  }];
4856
}
4857
 
4858
/* global stripHash: true */
4859
 
4860
/**
4861
 * ! This is a private undocumented service !
4862
 *
4863
 * @name $browser
4864
 * @requires $log
4865
 * @description
4866
 * This object has two goals:
4867
 *
4868
 * - hide all the global state in the browser caused by the window object
4869
 * - abstract away all the browser specific features and inconsistencies
4870
 *
4871
 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4872
 * service, which can be used for convenient testing of the application without the interaction with
4873
 * the real browser apis.
4874
 */
4875
/**
4876
 * @param {object} window The global window object.
4877
 * @param {object} document jQuery wrapped document.
4878
 * @param {object} $log window.console or an object with the same interface.
4879
 * @param {object} $sniffer $sniffer service
4880
 */
4881
function Browser(window, document, $log, $sniffer) {
4882
  var self = this,
4883
      rawDocument = document[0],
4884
      location = window.location,
4885
      history = window.history,
4886
      setTimeout = window.setTimeout,
4887
      clearTimeout = window.clearTimeout,
4888
      pendingDeferIds = {};
4889
 
4890
  self.isMock = false;
4891
 
4892
  var outstandingRequestCount = 0;
4893
  var outstandingRequestCallbacks = [];
4894
 
4895
  // TODO(vojta): remove this temporary api
4896
  self.$$completeOutstandingRequest = completeOutstandingRequest;
4897
  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4898
 
4899
  /**
4900
   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4901
   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4902
   */
4903
  function completeOutstandingRequest(fn) {
4904
    try {
4905
      fn.apply(null, sliceArgs(arguments, 1));
4906
    } finally {
4907
      outstandingRequestCount--;
4908
      if (outstandingRequestCount === 0) {
4909
        while (outstandingRequestCallbacks.length) {
4910
          try {
4911
            outstandingRequestCallbacks.pop()();
4912
          } catch (e) {
4913
            $log.error(e);
4914
          }
4915
        }
4916
      }
4917
    }
4918
  }
4919
 
4920
  function getHash(url) {
4921
    var index = url.indexOf('#');
4922
    return index === -1 ? '' : url.substr(index + 1);
4923
  }
4924
 
4925
  /**
4926
   * @private
4927
   * Note: this method is used only by scenario runner
4928
   * TODO(vojta): prefix this method with $$ ?
4929
   * @param {function()} callback Function that will be called when no outstanding request
4930
   */
4931
  self.notifyWhenNoOutstandingRequests = function(callback) {
4932
    // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4933
    // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4934
    // regular poller would result in flaky tests.
4935
    forEach(pollFns, function(pollFn) { pollFn(); });
4936
 
4937
    if (outstandingRequestCount === 0) {
4938
      callback();
4939
    } else {
4940
      outstandingRequestCallbacks.push(callback);
4941
    }
4942
  };
4943
 
4944
  //////////////////////////////////////////////////////////////
4945
  // Poll Watcher API
4946
  //////////////////////////////////////////////////////////////
4947
  var pollFns = [],
4948
      pollTimeout;
4949
 
4950
  /**
4951
   * @name $browser#addPollFn
4952
   *
4953
   * @param {function()} fn Poll function to add
4954
   *
4955
   * @description
4956
   * Adds a function to the list of functions that poller periodically executes,
4957
   * and starts polling if not started yet.
4958
   *
4959
   * @returns {function()} the added function
4960
   */
4961
  self.addPollFn = function(fn) {
4962
    if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
4963
    pollFns.push(fn);
4964
    return fn;
4965
  };
4966
 
4967
  /**
4968
   * @param {number} interval How often should browser call poll functions (ms)
4969
   * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
4970
   *
4971
   * @description
4972
   * Configures the poller to run in the specified intervals, using the specified
4973
   * setTimeout fn and kicks it off.
4974
   */
4975
  function startPoller(interval, setTimeout) {
4976
    (function check() {
4977
      forEach(pollFns, function(pollFn) { pollFn(); });
4978
      pollTimeout = setTimeout(check, interval);
4979
    })();
4980
  }
4981
 
4982
  //////////////////////////////////////////////////////////////
4983
  // URL API
4984
  //////////////////////////////////////////////////////////////
4985
 
4986
  var cachedState, lastHistoryState,
4987
      lastBrowserUrl = location.href,
4988
      baseElement = document.find('base'),
4989
      reloadLocation = null;
4990
 
4991
  cacheState();
4992
  lastHistoryState = cachedState;
4993
 
4994
  /**
4995
   * @name $browser#url
4996
   *
4997
   * @description
4998
   * GETTER:
4999
   * Without any argument, this method just returns current value of location.href.
5000
   *
5001
   * SETTER:
5002
   * With at least one argument, this method sets url to new value.
5003
   * If html5 history api supported, pushState/replaceState is used, otherwise
5004
   * location.href/location.replace is used.
5005
   * Returns its own instance to allow chaining
5006
   *
5007
   * NOTE: this api is intended for use only by the $location service. Please use the
5008
   * {@link ng.$location $location service} to change url.
5009
   *
5010
   * @param {string} url New url (when used as setter)
5011
   * @param {boolean=} replace Should new url replace current history record?
5012
   * @param {object=} state object to use with pushState/replaceState
5013
   */
5014
  self.url = function(url, replace, state) {
5015
    // In modern browsers `history.state` is `null` by default; treating it separately
5016
    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5017
    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5018
    if (isUndefined(state)) {
5019
      state = null;
5020
    }
5021
 
5022
    // Android Browser BFCache causes location, history reference to become stale.
5023
    if (location !== window.location) location = window.location;
5024
    if (history !== window.history) history = window.history;
5025
 
5026
    // setter
5027
    if (url) {
5028
      var sameState = lastHistoryState === state;
5029
 
5030
      // Don't change anything if previous and current URLs and states match. This also prevents
5031
      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5032
      // See https://github.com/angular/angular.js/commit/ffb2701
5033
      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5034
        return self;
5035
      }
5036
      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5037
      lastBrowserUrl = url;
5038
      lastHistoryState = state;
5039
      // Don't use history API if only the hash changed
5040
      // due to a bug in IE10/IE11 which leads
5041
      // to not firing a `hashchange` nor `popstate` event
5042
      // in some cases (see #9143).
5043
      if ($sniffer.history && (!sameBase || !sameState)) {
5044
        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5045
        cacheState();
5046
        // Do the assignment again so that those two variables are referentially identical.
5047
        lastHistoryState = cachedState;
5048
      } else {
5049
        if (!sameBase) {
5050
          reloadLocation = url;
5051
        }
5052
        if (replace) {
5053
          location.replace(url);
5054
        } else if (!sameBase) {
5055
          location.href = url;
5056
        } else {
5057
          location.hash = getHash(url);
5058
        }
5059
      }
5060
      return self;
5061
    // getter
5062
    } else {
5063
      // - reloadLocation is needed as browsers don't allow to read out
5064
      //   the new location.href if a reload happened.
5065
      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5066
      return reloadLocation || location.href.replace(/%27/g,"'");
5067
    }
5068
  };
5069
 
5070
  /**
5071
   * @name $browser#state
5072
   *
5073
   * @description
5074
   * This method is a getter.
5075
   *
5076
   * Return history.state or null if history.state is undefined.
5077
   *
5078
   * @returns {object} state
5079
   */
5080
  self.state = function() {
5081
    return cachedState;
5082
  };
5083
 
5084
  var urlChangeListeners = [],
5085
      urlChangeInit = false;
5086
 
5087
  function cacheStateAndFireUrlChange() {
5088
    cacheState();
5089
    fireUrlChange();
5090
  }
5091
 
5092
  // This variable should be used *only* inside the cacheState function.
5093
  var lastCachedState = null;
5094
  function cacheState() {
5095
    // This should be the only place in $browser where `history.state` is read.
5096
    cachedState = window.history.state;
5097
    cachedState = isUndefined(cachedState) ? null : cachedState;
5098
 
5099
    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5100
    if (equals(cachedState, lastCachedState)) {
5101
      cachedState = lastCachedState;
5102
    }
5103
    lastCachedState = cachedState;
5104
  }
5105
 
5106
  function fireUrlChange() {
5107
    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5108
      return;
5109
    }
5110
 
5111
    lastBrowserUrl = self.url();
5112
    lastHistoryState = cachedState;
5113
    forEach(urlChangeListeners, function(listener) {
5114
      listener(self.url(), cachedState);
5115
    });
5116
  }
5117
 
5118
  /**
5119
   * @name $browser#onUrlChange
5120
   *
5121
   * @description
5122
   * Register callback function that will be called, when url changes.
5123
   *
5124
   * It's only called when the url is changed from outside of angular:
5125
   * - user types different url into address bar
5126
   * - user clicks on history (forward/back) button
5127
   * - user clicks on a link
5128
   *
5129
   * It's not called when url is changed by $browser.url() method
5130
   *
5131
   * The listener gets called with new url as parameter.
5132
   *
5133
   * NOTE: this api is intended for use only by the $location service. Please use the
5134
   * {@link ng.$location $location service} to monitor url changes in angular apps.
5135
   *
5136
   * @param {function(string)} listener Listener function to be called when url changes.
5137
   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5138
   */
5139
  self.onUrlChange = function(callback) {
5140
    // TODO(vojta): refactor to use node's syntax for events
5141
    if (!urlChangeInit) {
5142
      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5143
      // don't fire popstate when user change the address bar and don't fire hashchange when url
5144
      // changed by push/replaceState
5145
 
5146
      // html5 history api - popstate event
5147
      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5148
      // hashchange event
5149
      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5150
 
5151
      urlChangeInit = true;
5152
    }
5153
 
5154
    urlChangeListeners.push(callback);
5155
    return callback;
5156
  };
5157
 
5158
  /**
5159
   * Checks whether the url has changed outside of Angular.
5160
   * Needs to be exported to be able to check for changes that have been done in sync,
5161
   * as hashchange/popstate events fire in async.
5162
   */
5163
  self.$$checkUrlChange = fireUrlChange;
5164
 
5165
  //////////////////////////////////////////////////////////////
5166
  // Misc API
5167
  //////////////////////////////////////////////////////////////
5168
 
5169
  /**
5170
   * @name $browser#baseHref
5171
   *
5172
   * @description
5173
   * Returns current <base href>
5174
   * (always relative - without domain)
5175
   *
5176
   * @returns {string} The current base href
5177
   */
5178
  self.baseHref = function() {
5179
    var href = baseElement.attr('href');
5180
    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5181
  };
5182
 
5183
  //////////////////////////////////////////////////////////////
5184
  // Cookies API
5185
  //////////////////////////////////////////////////////////////
5186
  var lastCookies = {};
5187
  var lastCookieString = '';
5188
  var cookiePath = self.baseHref();
5189
 
5190
  function safeDecodeURIComponent(str) {
5191
    try {
5192
      return decodeURIComponent(str);
5193
    } catch (e) {
5194
      return str;
5195
    }
5196
  }
5197
 
5198
  /**
5199
   * @name $browser#cookies
5200
   *
5201
   * @param {string=} name Cookie name
5202
   * @param {string=} value Cookie value
5203
   *
5204
   * @description
5205
   * The cookies method provides a 'private' low level access to browser cookies.
5206
   * It is not meant to be used directly, use the $cookie service instead.
5207
   *
5208
   * The return values vary depending on the arguments that the method was called with as follows:
5209
   *
5210
   * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
5211
   *   it
5212
   * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
5213
   * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
5214
   *   way)
5215
   *
5216
   * @returns {Object} Hash of all cookies (if called without any parameter)
5217
   */
5218
  self.cookies = function(name, value) {
5219
    var cookieLength, cookieArray, cookie, i, index;
5220
 
5221
    if (name) {
5222
      if (value === undefined) {
5223
        rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
5224
                                ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
5225
      } else {
5226
        if (isString(value)) {
5227
          cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
5228
                                ';path=' + cookiePath).length + 1;
5229
 
5230
          // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
5231
          // - 300 cookies
5232
          // - 20 cookies per unique domain
5233
          // - 4096 bytes per cookie
5234
          if (cookieLength > 4096) {
5235
            $log.warn("Cookie '" + name +
5236
              "' possibly not set or overflowed because it was too large (" +
5237
              cookieLength + " > 4096 bytes)!");
5238
          }
5239
        }
5240
      }
5241
    } else {
5242
      if (rawDocument.cookie !== lastCookieString) {
5243
        lastCookieString = rawDocument.cookie;
5244
        cookieArray = lastCookieString.split("; ");
5245
        lastCookies = {};
5246
 
5247
        for (i = 0; i < cookieArray.length; i++) {
5248
          cookie = cookieArray[i];
5249
          index = cookie.indexOf('=');
5250
          if (index > 0) { //ignore nameless cookies
5251
            name = safeDecodeURIComponent(cookie.substring(0, index));
5252
            // the first value that is seen for a cookie is the most
5253
            // specific one.  values for the same cookie name that
5254
            // follow are for less specific paths.
5255
            if (lastCookies[name] === undefined) {
5256
              lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
5257
            }
5258
          }
5259
        }
5260
      }
5261
      return lastCookies;
5262
    }
5263
  };
5264
 
5265
 
5266
  /**
5267
   * @name $browser#defer
5268
   * @param {function()} fn A function, who's execution should be deferred.
5269
   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5270
   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5271
   *
5272
   * @description
5273
   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5274
   *
5275
   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5276
   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5277
   * via `$browser.defer.flush()`.
5278
   *
5279
   */
5280
  self.defer = function(fn, delay) {
5281
    var timeoutId;
5282
    outstandingRequestCount++;
5283
    timeoutId = setTimeout(function() {
5284
      delete pendingDeferIds[timeoutId];
5285
      completeOutstandingRequest(fn);
5286
    }, delay || 0);
5287
    pendingDeferIds[timeoutId] = true;
5288
    return timeoutId;
5289
  };
5290
 
5291
 
5292
  /**
5293
   * @name $browser#defer.cancel
5294
   *
5295
   * @description
5296
   * Cancels a deferred task identified with `deferId`.
5297
   *
5298
   * @param {*} deferId Token returned by the `$browser.defer` function.
5299
   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5300
   *                    canceled.
5301
   */
5302
  self.defer.cancel = function(deferId) {
5303
    if (pendingDeferIds[deferId]) {
5304
      delete pendingDeferIds[deferId];
5305
      clearTimeout(deferId);
5306
      completeOutstandingRequest(noop);
5307
      return true;
5308
    }
5309
    return false;
5310
  };
5311
 
5312
}
5313
 
5314
function $BrowserProvider() {
5315
  this.$get = ['$window', '$log', '$sniffer', '$document',
5316
      function($window, $log, $sniffer, $document) {
5317
        return new Browser($window, $document, $log, $sniffer);
5318
      }];
5319
}
5320
 
5321
/**
5322
 * @ngdoc service
5323
 * @name $cacheFactory
5324
 *
5325
 * @description
5326
 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5327
 * them.
5328
 *
5329
 * ```js
5330
 *
5331
 *  var cache = $cacheFactory('cacheId');
5332
 *  expect($cacheFactory.get('cacheId')).toBe(cache);
5333
 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5334
 *
5335
 *  cache.put("key", "value");
5336
 *  cache.put("another key", "another value");
5337
 *
5338
 *  // We've specified no options on creation
5339
 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5340
 *
5341
 * ```
5342
 *
5343
 *
5344
 * @param {string} cacheId Name or id of the newly created cache.
5345
 * @param {object=} options Options object that specifies the cache behavior. Properties:
5346
 *
5347
 *   - `{number=}` `capacity` — turns the cache into LRU cache.
5348
 *
5349
 * @returns {object} Newly created cache object with the following set of methods:
5350
 *
5351
 * - `{object}` `info()` — Returns id, size, and options of cache.
5352
 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5353
 *   it.
5354
 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5355
 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5356
 * - `{void}` `removeAll()` — Removes all cached values.
5357
 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5358
 *
5359
 * @example
5360
   <example module="cacheExampleApp">
5361
     <file name="index.html">
5362
       <div ng-controller="CacheController">
5363
         <input ng-model="newCacheKey" placeholder="Key">
5364
         <input ng-model="newCacheValue" placeholder="Value">
5365
         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5366
 
5367
         <p ng-if="keys.length">Cached Values</p>
5368
         <div ng-repeat="key in keys">
5369
           <span ng-bind="key"></span>
5370
           <span>: </span>
5371
           <b ng-bind="cache.get(key)"></b>
5372
         </div>
5373
 
5374
         <p>Cache Info</p>
5375
         <div ng-repeat="(key, value) in cache.info()">
5376
           <span ng-bind="key"></span>
5377
           <span>: </span>
5378
           <b ng-bind="value"></b>
5379
         </div>
5380
       </div>
5381
     </file>
5382
     <file name="script.js">
5383
       angular.module('cacheExampleApp', []).
5384
         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5385
           $scope.keys = [];
5386
           $scope.cache = $cacheFactory('cacheId');
5387
           $scope.put = function(key, value) {
5388
             if ($scope.cache.get(key) === undefined) {
5389
               $scope.keys.push(key);
5390
             }
5391
             $scope.cache.put(key, value === undefined ? null : value);
5392
           };
5393
         }]);
5394
     </file>
5395
     <file name="style.css">
5396
       p {
5397
         margin: 10px 0 3px;
5398
       }
5399
     </file>
5400
   </example>
5401
 */
5402
function $CacheFactoryProvider() {
5403
 
5404
  this.$get = function() {
5405
    var caches = {};
5406
 
5407
    function cacheFactory(cacheId, options) {
5408
      if (cacheId in caches) {
5409
        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5410
      }
5411
 
5412
      var size = 0,
5413
          stats = extend({}, options, {id: cacheId}),
5414
          data = {},
5415
          capacity = (options && options.capacity) || Number.MAX_VALUE,
5416
          lruHash = {},
5417
          freshEnd = null,
5418
          staleEnd = null;
5419
 
5420
      /**
5421
       * @ngdoc type
5422
       * @name $cacheFactory.Cache
5423
       *
5424
       * @description
5425
       * A cache object used to store and retrieve data, primarily used by
5426
       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5427
       * templates and other data.
5428
       *
5429
       * ```js
5430
       *  angular.module('superCache')
5431
       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5432
       *      return $cacheFactory('super-cache');
5433
       *    }]);
5434
       * ```
5435
       *
5436
       * Example test:
5437
       *
5438
       * ```js
5439
       *  it('should behave like a cache', inject(function(superCache) {
5440
       *    superCache.put('key', 'value');
5441
       *    superCache.put('another key', 'another value');
5442
       *
5443
       *    expect(superCache.info()).toEqual({
5444
       *      id: 'super-cache',
5445
       *      size: 2
5446
       *    });
5447
       *
5448
       *    superCache.remove('another key');
5449
       *    expect(superCache.get('another key')).toBeUndefined();
5450
       *
5451
       *    superCache.removeAll();
5452
       *    expect(superCache.info()).toEqual({
5453
       *      id: 'super-cache',
5454
       *      size: 0
5455
       *    });
5456
       *  }));
5457
       * ```
5458
       */
5459
      return caches[cacheId] = {
5460
 
5461
        /**
5462
         * @ngdoc method
5463
         * @name $cacheFactory.Cache#put
5464
         * @kind function
5465
         *
5466
         * @description
5467
         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5468
         * retrieved later, and incrementing the size of the cache if the key was not already
5469
         * present in the cache. If behaving like an LRU cache, it will also remove stale
5470
         * entries from the set.
5471
         *
5472
         * It will not insert undefined values into the cache.
5473
         *
5474
         * @param {string} key the key under which the cached data is stored.
5475
         * @param {*} value the value to store alongside the key. If it is undefined, the key
5476
         *    will not be stored.
5477
         * @returns {*} the value stored.
5478
         */
5479
        put: function(key, value) {
5480
          if (capacity < Number.MAX_VALUE) {
5481
            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5482
 
5483
            refresh(lruEntry);
5484
          }
5485
 
5486
          if (isUndefined(value)) return;
5487
          if (!(key in data)) size++;
5488
          data[key] = value;
5489
 
5490
          if (size > capacity) {
5491
            this.remove(staleEnd.key);
5492
          }
5493
 
5494
          return value;
5495
        },
5496
 
5497
        /**
5498
         * @ngdoc method
5499
         * @name $cacheFactory.Cache#get
5500
         * @kind function
5501
         *
5502
         * @description
5503
         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5504
         *
5505
         * @param {string} key the key of the data to be retrieved
5506
         * @returns {*} the value stored.
5507
         */
5508
        get: function(key) {
5509
          if (capacity < Number.MAX_VALUE) {
5510
            var lruEntry = lruHash[key];
5511
 
5512
            if (!lruEntry) return;
5513
 
5514
            refresh(lruEntry);
5515
          }
5516
 
5517
          return data[key];
5518
        },
5519
 
5520
 
5521
        /**
5522
         * @ngdoc method
5523
         * @name $cacheFactory.Cache#remove
5524
         * @kind function
5525
         *
5526
         * @description
5527
         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5528
         *
5529
         * @param {string} key the key of the entry to be removed
5530
         */
5531
        remove: function(key) {
5532
          if (capacity < Number.MAX_VALUE) {
5533
            var lruEntry = lruHash[key];
5534
 
5535
            if (!lruEntry) return;
5536
 
5537
            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5538
            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5539
            link(lruEntry.n,lruEntry.p);
5540
 
5541
            delete lruHash[key];
5542
          }
5543
 
5544
          delete data[key];
5545
          size--;
5546
        },
5547
 
5548
 
5549
        /**
5550
         * @ngdoc method
5551
         * @name $cacheFactory.Cache#removeAll
5552
         * @kind function
5553
         *
5554
         * @description
5555
         * Clears the cache object of any entries.
5556
         */
5557
        removeAll: function() {
5558
          data = {};
5559
          size = 0;
5560
          lruHash = {};
5561
          freshEnd = staleEnd = null;
5562
        },
5563
 
5564
 
5565
        /**
5566
         * @ngdoc method
5567
         * @name $cacheFactory.Cache#destroy
5568
         * @kind function
5569
         *
5570
         * @description
5571
         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5572
         * removing it from the {@link $cacheFactory $cacheFactory} set.
5573
         */
5574
        destroy: function() {
5575
          data = null;
5576
          stats = null;
5577
          lruHash = null;
5578
          delete caches[cacheId];
5579
        },
5580
 
5581
 
5582
        /**
5583
         * @ngdoc method
5584
         * @name $cacheFactory.Cache#info
5585
         * @kind function
5586
         *
5587
         * @description
5588
         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5589
         *
5590
         * @returns {object} an object with the following properties:
5591
         *   <ul>
5592
         *     <li>**id**: the id of the cache instance</li>
5593
         *     <li>**size**: the number of entries kept in the cache instance</li>
5594
         *     <li>**...**: any additional properties from the options object when creating the
5595
         *       cache.</li>
5596
         *   </ul>
5597
         */
5598
        info: function() {
5599
          return extend({}, stats, {size: size});
5600
        }
5601
      };
5602
 
5603
 
5604
      /**
5605
       * makes the `entry` the freshEnd of the LRU linked list
5606
       */
5607
      function refresh(entry) {
5608
        if (entry != freshEnd) {
5609
          if (!staleEnd) {
5610
            staleEnd = entry;
5611
          } else if (staleEnd == entry) {
5612
            staleEnd = entry.n;
5613
          }
5614
 
5615
          link(entry.n, entry.p);
5616
          link(entry, freshEnd);
5617
          freshEnd = entry;
5618
          freshEnd.n = null;
5619
        }
5620
      }
5621
 
5622
 
5623
      /**
5624
       * bidirectionally links two entries of the LRU linked list
5625
       */
5626
      function link(nextEntry, prevEntry) {
5627
        if (nextEntry != prevEntry) {
5628
          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5629
          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5630
        }
5631
      }
5632
    }
5633
 
5634
 
5635
  /**
5636
   * @ngdoc method
5637
   * @name $cacheFactory#info
5638
   *
5639
   * @description
5640
   * Get information about all the caches that have been created
5641
   *
5642
   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5643
   */
5644
    cacheFactory.info = function() {
5645
      var info = {};
5646
      forEach(caches, function(cache, cacheId) {
5647
        info[cacheId] = cache.info();
5648
      });
5649
      return info;
5650
    };
5651
 
5652
 
5653
  /**
5654
   * @ngdoc method
5655
   * @name $cacheFactory#get
5656
   *
5657
   * @description
5658
   * Get access to a cache object by the `cacheId` used when it was created.
5659
   *
5660
   * @param {string} cacheId Name or id of a cache to access.
5661
   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5662
   */
5663
    cacheFactory.get = function(cacheId) {
5664
      return caches[cacheId];
5665
    };
5666
 
5667
 
5668
    return cacheFactory;
5669
  };
5670
}
5671
 
5672
/**
5673
 * @ngdoc service
5674
 * @name $templateCache
5675
 *
5676
 * @description
5677
 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5678
 * can load templates directly into the cache in a `script` tag, or by consuming the
5679
 * `$templateCache` service directly.
5680
 *
5681
 * Adding via the `script` tag:
5682
 *
5683
 * ```html
5684
 *   <script type="text/ng-template" id="templateId.html">
5685
 *     <p>This is the content of the template</p>
5686
 *   </script>
5687
 * ```
5688
 *
5689
 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5690
 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
5691
 * element with ng-app attribute), otherwise the template will be ignored.
5692
 *
5693
 * Adding via the $templateCache service:
5694
 *
5695
 * ```js
5696
 * var myApp = angular.module('myApp', []);
5697
 * myApp.run(function($templateCache) {
5698
 *   $templateCache.put('templateId.html', 'This is the content of the template');
5699
 * });
5700
 * ```
5701
 *
5702
 * To retrieve the template later, simply use it in your HTML:
5703
 * ```html
5704
 * <div ng-include=" 'templateId.html' "></div>
5705
 * ```
5706
 *
5707
 * or get it via Javascript:
5708
 * ```js
5709
 * $templateCache.get('templateId.html')
5710
 * ```
5711
 *
5712
 * See {@link ng.$cacheFactory $cacheFactory}.
5713
 *
5714
 */
5715
function $TemplateCacheProvider() {
5716
  this.$get = ['$cacheFactory', function($cacheFactory) {
5717
    return $cacheFactory('templates');
5718
  }];
5719
}
5720
 
5721
/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5722
 *
5723
 * DOM-related variables:
5724
 *
5725
 * - "node" - DOM Node
5726
 * - "element" - DOM Element or Node
5727
 * - "$node" or "$element" - jqLite-wrapped node or element
5728
 *
5729
 *
5730
 * Compiler related stuff:
5731
 *
5732
 * - "linkFn" - linking fn of a single directive
5733
 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5734
 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
5735
 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5736
 */
5737
 
5738
 
5739
/**
5740
 * @ngdoc service
5741
 * @name $compile
5742
 * @kind function
5743
 *
5744
 * @description
5745
 * Compiles an HTML string or DOM into a template and produces a template function, which
5746
 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5747
 *
5748
 * The compilation is a process of walking the DOM tree and matching DOM elements to
5749
 * {@link ng.$compileProvider#directive directives}.
5750
 *
5751
 * <div class="alert alert-warning">
5752
 * **Note:** This document is an in-depth reference of all directive options.
5753
 * For a gentle introduction to directives with examples of common use cases,
5754
 * see the {@link guide/directive directive guide}.
5755
 * </div>
5756
 *
5757
 * ## Comprehensive Directive API
5758
 *
5759
 * There are many different options for a directive.
5760
 *
5761
 * The difference resides in the return value of the factory function.
5762
 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5763
 * or just the `postLink` function (all other properties will have the default values).
5764
 *
5765
 * <div class="alert alert-success">
5766
 * **Best Practice:** It's recommended to use the "directive definition object" form.
5767
 * </div>
5768
 *
5769
 * Here's an example directive declared with a Directive Definition Object:
5770
 *
5771
 * ```js
5772
 *   var myModule = angular.module(...);
5773
 *
5774
 *   myModule.directive('directiveName', function factory(injectables) {
5775
 *     var directiveDefinitionObject = {
5776
 *       priority: 0,
5777
 *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5778
 *       // or
5779
 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5780
 *       transclude: false,
5781
 *       restrict: 'A',
5782
 *       templateNamespace: 'html',
5783
 *       scope: false,
5784
 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5785
 *       controllerAs: 'stringAlias',
5786
 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5787
 *       compile: function compile(tElement, tAttrs, transclude) {
5788
 *         return {
5789
 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5790
 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
5791
 *         }
5792
 *         // or
5793
 *         // return function postLink( ... ) { ... }
5794
 *       },
5795
 *       // or
5796
 *       // link: {
5797
 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5798
 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
5799
 *       // }
5800
 *       // or
5801
 *       // link: function postLink( ... ) { ... }
5802
 *     };
5803
 *     return directiveDefinitionObject;
5804
 *   });
5805
 * ```
5806
 *
5807
 * <div class="alert alert-warning">
5808
 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5809
 * </div>
5810
 *
5811
 * Therefore the above can be simplified as:
5812
 *
5813
 * ```js
5814
 *   var myModule = angular.module(...);
5815
 *
5816
 *   myModule.directive('directiveName', function factory(injectables) {
5817
 *     var directiveDefinitionObject = {
5818
 *       link: function postLink(scope, iElement, iAttrs) { ... }
5819
 *     };
5820
 *     return directiveDefinitionObject;
5821
 *     // or
5822
 *     // return function postLink(scope, iElement, iAttrs) { ... }
5823
 *   });
5824
 * ```
5825
 *
5826
 *
5827
 *
5828
 * ### Directive Definition Object
5829
 *
5830
 * The directive definition object provides instructions to the {@link ng.$compile
5831
 * compiler}. The attributes are:
5832
 *
5833
 * #### `multiElement`
5834
 * When this property is set to true, the HTML compiler will collect DOM nodes between
5835
 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5836
 * together as the directive elements. It is recommended that this feature be used on directives
5837
 * which are not strictly behavioural (such as {@link ngClick}), and which
5838
 * do not manipulate or replace child nodes (such as {@link ngInclude}).
5839
 *
5840
 * #### `priority`
5841
 * When there are multiple directives defined on a single DOM element, sometimes it
5842
 * is necessary to specify the order in which the directives are applied. The `priority` is used
5843
 * to sort the directives before their `compile` functions get called. Priority is defined as a
5844
 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5845
 * are also run in priority order, but post-link functions are run in reverse order. The order
5846
 * of directives with the same priority is undefined. The default priority is `0`.
5847
 *
5848
 * #### `terminal`
5849
 * If set to true then the current `priority` will be the last set of directives
5850
 * which will execute (any directives at the current priority will still execute
5851
 * as the order of execution on same `priority` is undefined). Note that expressions
5852
 * and other directives used in the directive's template will also be excluded from execution.
5853
 *
5854
 * #### `scope`
5855
 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5856
 * same element request a new scope, only one new scope is created. The new scope rule does not
5857
 * apply for the root of the template since the root of the template always gets a new scope.
5858
 *
5859
 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5860
 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5861
 * when creating reusable components, which should not accidentally read or modify data in the
5862
 * parent scope.
5863
 *
5864
 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5865
 * derived from the parent scope. These local properties are useful for aliasing values for
5866
 * templates. Locals definition is a hash of local scope property to its source:
5867
 *
5868
 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5869
 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
5870
 *   attribute name is assumed to be the same as the local name.
5871
 *   Given `<widget my-attr="hello {{name}}">` and widget definition
5872
 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5873
 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5874
 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
5875
 *   component scope).
5876
 *
5877
 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5878
 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5879
 *   name is specified then the attribute name is assumed to be the same as the local name.
5880
 *   Given `<widget my-attr="parentModel">` and widget definition of
5881
 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5882
 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5883
 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5884
 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5885
 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
5886
 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
5887
 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
5888
 *
5889
 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5890
 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
5891
 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
5892
 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5893
 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
5894
 *   pass data from the isolated scope via an expression to the parent scope, this can be
5895
 *   done by passing a map of local variable names and values into the expression wrapper fn.
5896
 *   For example, if the expression is `increment(amount)` then we can specify the amount value
5897
 *   by calling the `localFn` as `localFn({amount: 22})`.
5898
 *
5899
 *
5900
 * #### `bindToController`
5901
 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5902
 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5903
 * is instantiated, the initial values of the isolate scope bindings are already available.
5904
 *
5905
 * #### `controller`
5906
 * Controller constructor function. The controller is instantiated before the
5907
 * pre-linking phase and it is shared with other directives (see
5908
 * `require` attribute). This allows the directives to communicate with each other and augment
5909
 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5910
 *
5911
 * * `$scope` - Current scope associated with the element
5912
 * * `$element` - Current element
5913
 * * `$attrs` - Current attributes object for the element
5914
 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
5915
 *   `function([scope], cloneLinkingFn, futureParentElement)`.
5916
 *    * `scope`: optional argument to override the scope.
5917
 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
5918
 *    * `futureParentElement`:
5919
 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
5920
 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
5921
 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
5922
 *          and when the `cloneLinkinFn` is passed,
5923
 *          as those elements need to created and cloned in a special way when they are defined outside their
5924
 *          usual containers (e.g. like `<svg>`).
5925
 *        * See also the `directive.templateNamespace` property.
5926
 *
5927
 *
5928
 * #### `require`
5929
 * Require another directive and inject its controller as the fourth argument to the linking function. The
5930
 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5931
 * injected argument will be an array in corresponding order. If no such directive can be
5932
 * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
5933
 *
5934
 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5935
 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5936
 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5937
 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
5938
 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5939
 *   `null` to the `link` fn if not found.
5940
 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
5941
 *   `null` to the `link` fn if not found.
5942
 *
5943
 *
5944
 * #### `controllerAs`
5945
 * Controller alias at the directive scope. An alias for the controller so it
5946
 * can be referenced at the directive template. The directive needs to define a scope for this
5947
 * configuration to be used. Useful in the case when directive is used as component.
5948
 *
5949
 *
5950
 * #### `restrict`
5951
 * String of subset of `EACM` which restricts the directive to a specific directive
5952
 * declaration style. If omitted, the defaults (elements and attributes) are used.
5953
 *
5954
 * * `E` - Element name (default): `<my-directive></my-directive>`
5955
 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5956
 * * `C` - Class: `<div class="my-directive: exp;"></div>`
5957
 * * `M` - Comment: `<!-- directive: my-directive exp -->`
5958
 *
5959
 *
5960
 * #### `templateNamespace`
5961
 * String representing the document type used by the markup in the template.
5962
 * AngularJS needs this information as those elements need to be created and cloned
5963
 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
5964
 *
5965
 * * `html` - All root nodes in the template are HTML. Root nodes may also be
5966
 *   top-level elements such as `<svg>` or `<math>`.
5967
 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
5968
 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
5969
 *
5970
 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
5971
 *
5972
 * #### `template`
5973
 * HTML markup that may:
5974
 * * Replace the contents of the directive's element (default).
5975
 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5976
 * * Wrap the contents of the directive's element (if `transclude` is true).
5977
 *
5978
 * Value may be:
5979
 *
5980
 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5981
 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5982
 *   function api below) and returns a string value.
5983
 *
5984
 *
5985
 * #### `templateUrl`
5986
 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
5987
 *
5988
 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
5989
 * for later when the template has been resolved.  In the meantime it will continue to compile and link
5990
 * sibling and parent elements as though this element had not contained any directives.
5991
 *
5992
 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
5993
 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
5994
 * case when only one deeply nested directive has `templateUrl`.
5995
 *
5996
 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
5997
 *
5998
 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
5999
 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6000
 * a string value representing the url.  In either case, the template URL is passed through {@link
6001
 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6002
 *
6003
 *
6004
 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6005
 * specify what the template should replace. Defaults to `false`.
6006
 *
6007
 * * `true` - the template will replace the directive's element.
6008
 * * `false` - the template will replace the contents of the directive's element.
6009
 *
6010
 * The replacement process migrates all of the attributes / classes from the old element to the new
6011
 * one. See the {@link guide/directive#template-expanding-directive
6012
 * Directives Guide} for an example.
6013
 *
6014
 * There are very few scenarios where element replacement is required for the application function,
6015
 * the main one being reusable custom components that are used within SVG contexts
6016
 * (because SVG doesn't work with custom elements in the DOM tree).
6017
 *
6018
 * #### `transclude`
6019
 * Extract the contents of the element where the directive appears and make it available to the directive.
6020
 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6021
 * {@link $compile#transclusion Transclusion} section below.
6022
 *
6023
 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6024
 * directive's element or the entire element:
6025
 *
6026
 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6027
 * * `'element'` - transclude the whole of the directive's element including any directives on this
6028
 *   element that defined at a lower priority than this directive. When used, the `template`
6029
 *   property is ignored.
6030
 *
6031
 *
6032
 * #### `compile`
6033
 *
6034
 * ```js
6035
 *   function compile(tElement, tAttrs, transclude) { ... }
6036
 * ```
6037
 *
6038
 * The compile function deals with transforming the template DOM. Since most directives do not do
6039
 * template transformation, it is not used often. The compile function takes the following arguments:
6040
 *
6041
 *   * `tElement` - template element - The element where the directive has been declared. It is
6042
 *     safe to do template transformation on the element and child elements only.
6043
 *
6044
 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6045
 *     between all directive compile functions.
6046
 *
6047
 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6048
 *
6049
 * <div class="alert alert-warning">
6050
 * **Note:** The template instance and the link instance may be different objects if the template has
6051
 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6052
 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6053
 * should be done in a linking function rather than in a compile function.
6054
 * </div>
6055
 
6056
 * <div class="alert alert-warning">
6057
 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6058
 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6059
 * stack overflow errors.
6060
 *
6061
 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6062
 * a directive's template instead of relying on automatic template compilation via `template` or
6063
 * `templateUrl` declaration or manual compilation inside the compile function.
6064
 * </div>
6065
 *
6066
 * <div class="alert alert-error">
6067
 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6068
 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6069
 *   to the link function instead.
6070
 * </div>
6071
 
6072
 * A compile function can have a return value which can be either a function or an object.
6073
 *
6074
 * * returning a (post-link) function - is equivalent to registering the linking function via the
6075
 *   `link` property of the config object when the compile function is empty.
6076
 *
6077
 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6078
 *   control when a linking function should be called during the linking phase. See info about
6079
 *   pre-linking and post-linking functions below.
6080
 *
6081
 *
6082
 * #### `link`
6083
 * This property is used only if the `compile` property is not defined.
6084
 *
6085
 * ```js
6086
 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6087
 * ```
6088
 *
6089
 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6090
 * executed after the template has been cloned. This is where most of the directive logic will be
6091
 * put.
6092
 *
6093
 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6094
 *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6095
 *
6096
 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6097
 *     manipulate the children of the element only in `postLink` function since the children have
6098
 *     already been linked.
6099
 *
6100
 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6101
 *     between all directive linking functions.
6102
 *
6103
 *   * `controller` - a controller instance - A controller instance if at least one directive on the
6104
 *     element defines a controller. The controller is shared among all the directives, which allows
6105
 *     the directives to use the controllers as a communication channel.
6106
 *
6107
 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6108
 *     This is the same as the `$transclude`
6109
 *     parameter of directive controllers, see there for details.
6110
 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6111
 *
6112
 * #### Pre-linking function
6113
 *
6114
 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6115
 * compiler linking function will fail to locate the correct elements for linking.
6116
 *
6117
 * #### Post-linking function
6118
 *
6119
 * Executed after the child elements are linked.
6120
 *
6121
 * Note that child elements that contain `templateUrl` directives will not have been compiled
6122
 * and linked since they are waiting for their template to load asynchronously and their own
6123
 * compilation and linking has been suspended until that occurs.
6124
 *
6125
 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6126
 * for their async templates to be resolved.
6127
 *
6128
 *
6129
 * ### Transclusion
6130
 *
6131
 * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
6132
 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6133
 * scope from where they were taken.
6134
 *
6135
 * Transclusion is used (often with {@link ngTransclude}) to insert the
6136
 * original contents of a directive's element into a specified place in the template of the directive.
6137
 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6138
 * content has access to the properties on the scope from which it was taken, even if the directive
6139
 * has isolated scope.
6140
 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6141
 *
6142
 * This makes it possible for the widget to have private state for its template, while the transcluded
6143
 * content has access to its originating scope.
6144
 *
6145
 * <div class="alert alert-warning">
6146
 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6147
 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6148
 * Testing Transclusion Directives}.
6149
 * </div>
6150
 *
6151
 * #### Transclusion Functions
6152
 *
6153
 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6154
 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6155
 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6156
 *
6157
 * <div class="alert alert-info">
6158
 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6159
 * ngTransclude will deal with it for us.
6160
 * </div>
6161
 *
6162
 * If you want to manually control the insertion and removal of the transcluded content in your directive
6163
 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6164
 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6165
 *
6166
 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6167
 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6168
 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6169
 *
6170
 * <div class="alert alert-info">
6171
 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6172
 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6173
 * </div>
6174
 *
6175
 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6176
 * attach function**:
6177
 *
6178
 * ```js
6179
 * var transcludedContent, transclusionScope;
6180
 *
6181
 * $transclude(function(clone, scope) {
6182
 *   element.append(clone);
6183
 *   transcludedContent = clone;
6184
 *   transclusionScope = scope;
6185
 * });
6186
 * ```
6187
 *
6188
 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6189
 * associated transclusion scope:
6190
 *
6191
 * ```js
6192
 * transcludedContent.remove();
6193
 * transclusionScope.$destroy();
6194
 * ```
6195
 *
6196
 * <div class="alert alert-info">
6197
 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6198
 * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
6199
 * then you are also responsible for calling `$destroy` on the transclusion scope.
6200
 * </div>
6201
 *
6202
 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6203
 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6204
 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6205
 *
6206
 *
6207
 * #### Transclusion Scopes
6208
 *
6209
 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6210
 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6211
 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6212
 * was taken.
6213
 *
6214
 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6215
 * like this:
6216
 *
6217
 * ```html
6218
 * <div ng-app>
6219
 *   <div isolate>
6220
 *     <div transclusion>
6221
 *     </div>
6222
 *   </div>
6223
 * </div>
6224
 * ```
6225
 *
6226
 * The `$parent` scope hierarchy will look like this:
6227
 *
6228
 * ```
6229
 * - $rootScope
6230
 *   - isolate
6231
 *     - transclusion
6232
 * ```
6233
 *
6234
 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6235
 *
6236
 * ```
6237
 * - $rootScope
6238
 *   - transclusion
6239
 * - isolate
6240
 * ```
6241
 *
6242
 *
6243
 * ### Attributes
6244
 *
6245
 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6246
 * `link()` or `compile()` functions. It has a variety of uses.
6247
 *
6248
 * accessing *Normalized attribute names:*
6249
 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6250
 * the attributes object allows for normalized access to
6251
 *   the attributes.
6252
 *
6253
 * * *Directive inter-communication:* All directives share the same instance of the attributes
6254
 *   object which allows the directives to use the attributes object as inter directive
6255
 *   communication.
6256
 *
6257
 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6258
 *   allowing other directives to read the interpolated value.
6259
 *
6260
 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6261
 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6262
 *   the only way to easily get the actual value because during the linking phase the interpolation
6263
 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6264
 *
6265
 * ```js
6266
 * function linkingFn(scope, elm, attrs, ctrl) {
6267
 *   // get the attribute value
6268
 *   console.log(attrs.ngModel);
6269
 *
6270
 *   // change the attribute
6271
 *   attrs.$set('ngModel', 'new value');
6272
 *
6273
 *   // observe changes to interpolated attribute
6274
 *   attrs.$observe('ngModel', function(value) {
6275
 *     console.log('ngModel has changed value to ' + value);
6276
 *   });
6277
 * }
6278
 * ```
6279
 *
6280
 * ## Example
6281
 *
6282
 * <div class="alert alert-warning">
6283
 * **Note**: Typically directives are registered with `module.directive`. The example below is
6284
 * to illustrate how `$compile` works.
6285
 * </div>
6286
 *
6287
 <example module="compileExample">
6288
   <file name="index.html">
6289
    <script>
6290
      angular.module('compileExample', [], function($compileProvider) {
6291
        // configure new 'compile' directive by passing a directive
6292
        // factory function. The factory function injects the '$compile'
6293
        $compileProvider.directive('compile', function($compile) {
6294
          // directive factory creates a link function
6295
          return function(scope, element, attrs) {
6296
            scope.$watch(
6297
              function(scope) {
6298
                 // watch the 'compile' expression for changes
6299
                return scope.$eval(attrs.compile);
6300
              },
6301
              function(value) {
6302
                // when the 'compile' expression changes
6303
                // assign it into the current DOM
6304
                element.html(value);
6305
 
6306
                // compile the new DOM and link it to the current
6307
                // scope.
6308
                // NOTE: we only compile .childNodes so that
6309
                // we don't get into infinite loop compiling ourselves
6310
                $compile(element.contents())(scope);
6311
              }
6312
            );
6313
          };
6314
        });
6315
      })
6316
      .controller('GreeterController', ['$scope', function($scope) {
6317
        $scope.name = 'Angular';
6318
        $scope.html = 'Hello {{name}}';
6319
      }]);
6320
    </script>
6321
    <div ng-controller="GreeterController">
6322
      <input ng-model="name"> <br>
6323
      <textarea ng-model="html"></textarea> <br>
6324
      <div compile="html"></div>
6325
    </div>
6326
   </file>
6327
   <file name="protractor.js" type="protractor">
6328
     it('should auto compile', function() {
6329
       var textarea = $('textarea');
6330
       var output = $('div[compile]');
6331
       // The initial state reads 'Hello Angular'.
6332
       expect(output.getText()).toBe('Hello Angular');
6333
       textarea.clear();
6334
       textarea.sendKeys('{{name}}!');
6335
       expect(output.getText()).toBe('Angular!');
6336
     });
6337
   </file>
6338
 </example>
6339
 
6340
 *
6341
 *
6342
 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6343
 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6344
 *
6345
 * <div class="alert alert-error">
6346
 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6347
 *   e.g. will not use the right outer scope. Please pass the transclude function as a
6348
 *   `parentBoundTranscludeFn` to the link function instead.
6349
 * </div>
6350
 *
6351
 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6352
 *                 root element(s), not their children)
6353
 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6354
 * (a DOM element/tree) to a scope. Where:
6355
 *
6356
 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6357
 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6358
 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
6359
 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6360
 *  called as: <br> `cloneAttachFn(clonedElement, scope)` where:
6361
 *
6362
 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
6363
 *      * `scope` - is the current scope with which the linking function is working with.
6364
 *
6365
 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
6366
 *  keys may be used to control linking behavior:
6367
 *
6368
 *      * `parentBoundTranscludeFn` - the transclude function made available to
6369
 *        directives; if given, it will be passed through to the link functions of
6370
 *        directives found in `element` during compilation.
6371
 *      * `transcludeControllers` - an object hash with keys that map controller names
6372
 *        to controller instances; if given, it will make the controllers
6373
 *        available to directives.
6374
 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6375
 *        the cloned elements; only needed for transcludes that are allowed to contain non html
6376
 *        elements (e.g. SVG elements). See also the directive.controller property.
6377
 *
6378
 * Calling the linking function returns the element of the template. It is either the original
6379
 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6380
 *
6381
 * After linking the view is not updated until after a call to $digest which typically is done by
6382
 * Angular automatically.
6383
 *
6384
 * If you need access to the bound view, there are two ways to do it:
6385
 *
6386
 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6387
 *   before you send them to the compiler and keep this reference around.
6388
 *   ```js
6389
 *     var element = $compile('<p>{{total}}</p>')(scope);
6390
 *   ```
6391
 *
6392
 * - if on the other hand, you need the element to be cloned, the view reference from the original
6393
 *   example would not point to the clone, but rather to the original template that was cloned. In
6394
 *   this case, you can access the clone via the cloneAttachFn:
6395
 *   ```js
6396
 *     var templateElement = angular.element('<p>{{total}}</p>'),
6397
 *         scope = ....;
6398
 *
6399
 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6400
 *       //attach the clone to DOM document at the right place
6401
 *     });
6402
 *
6403
 *     //now we have reference to the cloned DOM via `clonedElement`
6404
 *   ```
6405
 *
6406
 *
6407
 * For information on how the compiler works, see the
6408
 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6409
 */
6410
 
6411
var $compileMinErr = minErr('$compile');
6412
 
6413
/**
6414
 * @ngdoc provider
6415
 * @name $compileProvider
6416
 *
6417
 * @description
6418
 */
6419
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6420
function $CompileProvider($provide, $$sanitizeUriProvider) {
6421
  var hasDirectives = {},
6422
      Suffix = 'Directive',
6423
      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6424
      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6425
      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6426
      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6427
 
6428
  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6429
  // The assumption is that future DOM event attribute names will begin with
6430
  // 'on' and be composed of only English letters.
6431
  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6432
 
6433
  function parseIsolateBindings(scope, directiveName) {
6434
    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6435
 
6436
    var bindings = {};
6437
 
6438
    forEach(scope, function(definition, scopeName) {
6439
      var match = definition.match(LOCAL_REGEXP);
6440
 
6441
      if (!match) {
6442
        throw $compileMinErr('iscp',
6443
            "Invalid isolate scope definition for directive '{0}'." +
6444
            " Definition: {... {1}: '{2}' ...}",
6445
            directiveName, scopeName, definition);
6446
      }
6447
 
6448
      bindings[scopeName] = {
6449
        mode: match[1][0],
6450
        collection: match[2] === '*',
6451
        optional: match[3] === '?',
6452
        attrName: match[4] || scopeName
6453
      };
6454
    });
6455
 
6456
    return bindings;
6457
  }
6458
 
6459
  /**
6460
   * @ngdoc method
6461
   * @name $compileProvider#directive
6462
   * @kind function
6463
   *
6464
   * @description
6465
   * Register a new directive with the compiler.
6466
   *
6467
   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6468
   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6469
   *    names and the values are the factories.
6470
   * @param {Function|Array} directiveFactory An injectable directive factory function. See
6471
   *    {@link guide/directive} for more info.
6472
   * @returns {ng.$compileProvider} Self for chaining.
6473
   */
6474
   this.directive = function registerDirective(name, directiveFactory) {
6475
    assertNotHasOwnProperty(name, 'directive');
6476
    if (isString(name)) {
6477
      assertArg(directiveFactory, 'directiveFactory');
6478
      if (!hasDirectives.hasOwnProperty(name)) {
6479
        hasDirectives[name] = [];
6480
        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6481
          function($injector, $exceptionHandler) {
6482
            var directives = [];
6483
            forEach(hasDirectives[name], function(directiveFactory, index) {
6484
              try {
6485
                var directive = $injector.invoke(directiveFactory);
6486
                if (isFunction(directive)) {
6487
                  directive = { compile: valueFn(directive) };
6488
                } else if (!directive.compile && directive.link) {
6489
                  directive.compile = valueFn(directive.link);
6490
                }
6491
                directive.priority = directive.priority || 0;
6492
                directive.index = index;
6493
                directive.name = directive.name || name;
6494
                directive.require = directive.require || (directive.controller && directive.name);
6495
                directive.restrict = directive.restrict || 'EA';
6496
                if (isObject(directive.scope)) {
6497
                  directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
6498
                }
6499
                directives.push(directive);
6500
              } catch (e) {
6501
                $exceptionHandler(e);
6502
              }
6503
            });
6504
            return directives;
6505
          }]);
6506
      }
6507
      hasDirectives[name].push(directiveFactory);
6508
    } else {
6509
      forEach(name, reverseParams(registerDirective));
6510
    }
6511
    return this;
6512
  };
6513
 
6514
 
6515
  /**
6516
   * @ngdoc method
6517
   * @name $compileProvider#aHrefSanitizationWhitelist
6518
   * @kind function
6519
   *
6520
   * @description
6521
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6522
   * urls during a[href] sanitization.
6523
   *
6524
   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6525
   *
6526
   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6527
   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6528
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6529
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6530
   *
6531
   * @param {RegExp=} regexp New regexp to whitelist urls with.
6532
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6533
   *    chaining otherwise.
6534
   */
6535
  this.aHrefSanitizationWhitelist = function(regexp) {
6536
    if (isDefined(regexp)) {
6537
      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6538
      return this;
6539
    } else {
6540
      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6541
    }
6542
  };
6543
 
6544
 
6545
  /**
6546
   * @ngdoc method
6547
   * @name $compileProvider#imgSrcSanitizationWhitelist
6548
   * @kind function
6549
   *
6550
   * @description
6551
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6552
   * urls during img[src] sanitization.
6553
   *
6554
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
6555
   *
6556
   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
6557
   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
6558
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6559
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6560
   *
6561
   * @param {RegExp=} regexp New regexp to whitelist urls with.
6562
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6563
   *    chaining otherwise.
6564
   */
6565
  this.imgSrcSanitizationWhitelist = function(regexp) {
6566
    if (isDefined(regexp)) {
6567
      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
6568
      return this;
6569
    } else {
6570
      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
6571
    }
6572
  };
6573
 
6574
  /**
6575
   * @ngdoc method
6576
   * @name  $compileProvider#debugInfoEnabled
6577
   *
6578
   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
6579
   * current debugInfoEnabled state
6580
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
6581
   *
6582
   * @kind function
6583
   *
6584
   * @description
6585
   * Call this method to enable/disable various debug runtime information in the compiler such as adding
6586
   * binding information and a reference to the current scope on to DOM elements.
6587
   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
6588
   * * `ng-binding` CSS class
6589
   * * `$binding` data property containing an array of the binding expressions
6590
   *
6591
   * You may want to disable this in production for a significant performance boost. See
6592
   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
6593
   *
6594
   * The default value is true.
6595
   */
6596
  var debugInfoEnabled = true;
6597
  this.debugInfoEnabled = function(enabled) {
6598
    if (isDefined(enabled)) {
6599
      debugInfoEnabled = enabled;
6600
      return this;
6601
    }
6602
    return debugInfoEnabled;
6603
  };
6604
 
6605
  this.$get = [
6606
            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
6607
            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
6608
    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
6609
             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
6610
 
6611
    var Attributes = function(element, attributesToCopy) {
6612
      if (attributesToCopy) {
6613
        var keys = Object.keys(attributesToCopy);
6614
        var i, l, key;
6615
 
6616
        for (i = 0, l = keys.length; i < l; i++) {
6617
          key = keys[i];
6618
          this[key] = attributesToCopy[key];
6619
        }
6620
      } else {
6621
        this.$attr = {};
6622
      }
6623
 
6624
      this.$$element = element;
6625
    };
6626
 
6627
    Attributes.prototype = {
6628
      /**
6629
       * @ngdoc method
6630
       * @name $compile.directive.Attributes#$normalize
6631
       * @kind function
6632
       *
6633
       * @description
6634
       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
6635
       * `data-`) to its normalized, camelCase form.
6636
       *
6637
       * Also there is special case for Moz prefix starting with upper case letter.
6638
       *
6639
       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
6640
       *
6641
       * @param {string} name Name to normalize
6642
       */
6643
      $normalize: directiveNormalize,
6644
 
6645
 
6646
      /**
6647
       * @ngdoc method
6648
       * @name $compile.directive.Attributes#$addClass
6649
       * @kind function
6650
       *
6651
       * @description
6652
       * Adds the CSS class value specified by the classVal parameter to the element. If animations
6653
       * are enabled then an animation will be triggered for the class addition.
6654
       *
6655
       * @param {string} classVal The className value that will be added to the element
6656
       */
6657
      $addClass: function(classVal) {
6658
        if (classVal && classVal.length > 0) {
6659
          $animate.addClass(this.$$element, classVal);
6660
        }
6661
      },
6662
 
6663
      /**
6664
       * @ngdoc method
6665
       * @name $compile.directive.Attributes#$removeClass
6666
       * @kind function
6667
       *
6668
       * @description
6669
       * Removes the CSS class value specified by the classVal parameter from the element. If
6670
       * animations are enabled then an animation will be triggered for the class removal.
6671
       *
6672
       * @param {string} classVal The className value that will be removed from the element
6673
       */
6674
      $removeClass: function(classVal) {
6675
        if (classVal && classVal.length > 0) {
6676
          $animate.removeClass(this.$$element, classVal);
6677
        }
6678
      },
6679
 
6680
      /**
6681
       * @ngdoc method
6682
       * @name $compile.directive.Attributes#$updateClass
6683
       * @kind function
6684
       *
6685
       * @description
6686
       * Adds and removes the appropriate CSS class values to the element based on the difference
6687
       * between the new and old CSS class values (specified as newClasses and oldClasses).
6688
       *
6689
       * @param {string} newClasses The current CSS className value
6690
       * @param {string} oldClasses The former CSS className value
6691
       */
6692
      $updateClass: function(newClasses, oldClasses) {
6693
        var toAdd = tokenDifference(newClasses, oldClasses);
6694
        if (toAdd && toAdd.length) {
6695
          $animate.addClass(this.$$element, toAdd);
6696
        }
6697
 
6698
        var toRemove = tokenDifference(oldClasses, newClasses);
6699
        if (toRemove && toRemove.length) {
6700
          $animate.removeClass(this.$$element, toRemove);
6701
        }
6702
      },
6703
 
6704
      /**
6705
       * Set a normalized attribute on the element in a way such that all directives
6706
       * can share the attribute. This function properly handles boolean attributes.
6707
       * @param {string} key Normalized key. (ie ngAttribute)
6708
       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
6709
       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
6710
       *     Defaults to true.
6711
       * @param {string=} attrName Optional none normalized name. Defaults to key.
6712
       */
6713
      $set: function(key, value, writeAttr, attrName) {
6714
        // TODO: decide whether or not to throw an error if "class"
6715
        //is set through this function since it may cause $updateClass to
6716
        //become unstable.
6717
 
6718
        var node = this.$$element[0],
6719
            booleanKey = getBooleanAttrName(node, key),
6720
            aliasedKey = getAliasedAttrName(node, key),
6721
            observer = key,
6722
            nodeName;
6723
 
6724
        if (booleanKey) {
6725
          this.$$element.prop(key, value);
6726
          attrName = booleanKey;
6727
        } else if (aliasedKey) {
6728
          this[aliasedKey] = value;
6729
          observer = aliasedKey;
6730
        }
6731
 
6732
        this[key] = value;
6733
 
6734
        // translate normalized key to actual key
6735
        if (attrName) {
6736
          this.$attr[key] = attrName;
6737
        } else {
6738
          attrName = this.$attr[key];
6739
          if (!attrName) {
6740
            this.$attr[key] = attrName = snake_case(key, '-');
6741
          }
6742
        }
6743
 
6744
        nodeName = nodeName_(this.$$element);
6745
 
6746
        if ((nodeName === 'a' && key === 'href') ||
6747
            (nodeName === 'img' && key === 'src')) {
6748
          // sanitize a[href] and img[src] values
6749
          this[key] = value = $$sanitizeUri(value, key === 'src');
6750
        } else if (nodeName === 'img' && key === 'srcset') {
6751
          // sanitize img[srcset] values
6752
          var result = "";
6753
 
6754
          // first check if there are spaces because it's not the same pattern
6755
          var trimmedSrcset = trim(value);
6756
          //                (   999x   ,|   999w   ,|   ,|,   )
6757
          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
6758
          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
6759
 
6760
          // split srcset into tuple of uri and descriptor except for the last item
6761
          var rawUris = trimmedSrcset.split(pattern);
6762
 
6763
          // for each tuples
6764
          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6765
          for (var i = 0; i < nbrUrisWith2parts; i++) {
6766
            var innerIdx = i * 2;
6767
            // sanitize the uri
6768
            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6769
            // add the descriptor
6770
            result += (" " + trim(rawUris[innerIdx + 1]));
6771
          }
6772
 
6773
          // split the last item into uri and descriptor
6774
          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6775
 
6776
          // sanitize the last uri
6777
          result += $$sanitizeUri(trim(lastTuple[0]), true);
6778
 
6779
          // and add the last descriptor if any
6780
          if (lastTuple.length === 2) {
6781
            result += (" " + trim(lastTuple[1]));
6782
          }
6783
          this[key] = value = result;
6784
        }
6785
 
6786
        if (writeAttr !== false) {
6787
          if (value === null || value === undefined) {
6788
            this.$$element.removeAttr(attrName);
6789
          } else {
6790
            this.$$element.attr(attrName, value);
6791
          }
6792
        }
6793
 
6794
        // fire observers
6795
        var $$observers = this.$$observers;
6796
        $$observers && forEach($$observers[observer], function(fn) {
6797
          try {
6798
            fn(value);
6799
          } catch (e) {
6800
            $exceptionHandler(e);
6801
          }
6802
        });
6803
      },
6804
 
6805
 
6806
      /**
6807
       * @ngdoc method
6808
       * @name $compile.directive.Attributes#$observe
6809
       * @kind function
6810
       *
6811
       * @description
6812
       * Observes an interpolated attribute.
6813
       *
6814
       * The observer function will be invoked once during the next `$digest` following
6815
       * compilation. The observer is then invoked whenever the interpolated value
6816
       * changes.
6817
       *
6818
       * @param {string} key Normalized key. (ie ngAttribute) .
6819
       * @param {function(interpolatedValue)} fn Function that will be called whenever
6820
                the interpolated value of the attribute changes.
6821
       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
6822
       * @returns {function()} Returns a deregistration function for this observer.
6823
       */
6824
      $observe: function(key, fn) {
6825
        var attrs = this,
6826
            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
6827
            listeners = ($$observers[key] || ($$observers[key] = []));
6828
 
6829
        listeners.push(fn);
6830
        $rootScope.$evalAsync(function() {
6831
          if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
6832
            // no one registered attribute interpolation function, so lets call it manually
6833
            fn(attrs[key]);
6834
          }
6835
        });
6836
 
6837
        return function() {
6838
          arrayRemove(listeners, fn);
6839
        };
6840
      }
6841
    };
6842
 
6843
 
6844
    function safeAddClass($element, className) {
6845
      try {
6846
        $element.addClass(className);
6847
      } catch (e) {
6848
        // ignore, since it means that we are trying to set class on
6849
        // SVG element, where class name is read-only.
6850
      }
6851
    }
6852
 
6853
 
6854
    var startSymbol = $interpolate.startSymbol(),
6855
        endSymbol = $interpolate.endSymbol(),
6856
        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
6857
            ? identity
6858
            : function denormalizeTemplate(template) {
6859
              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
6860
        },
6861
        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
6862
 
6863
    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
6864
      var bindings = $element.data('$binding') || [];
6865
 
6866
      if (isArray(binding)) {
6867
        bindings = bindings.concat(binding);
6868
      } else {
6869
        bindings.push(binding);
6870
      }
6871
 
6872
      $element.data('$binding', bindings);
6873
    } : noop;
6874
 
6875
    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
6876
      safeAddClass($element, 'ng-binding');
6877
    } : noop;
6878
 
6879
    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
6880
      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
6881
      $element.data(dataName, scope);
6882
    } : noop;
6883
 
6884
    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
6885
      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
6886
    } : noop;
6887
 
6888
    return compile;
6889
 
6890
    //================================
6891
 
6892
    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
6893
                        previousCompileContext) {
6894
      if (!($compileNodes instanceof jqLite)) {
6895
        // jquery always rewraps, whereas we need to preserve the original selector so that we can
6896
        // modify it.
6897
        $compileNodes = jqLite($compileNodes);
6898
      }
6899
      // We can not compile top level text elements since text nodes can be merged and we will
6900
      // not be able to attach scope data to them, so we will wrap them in <span>
6901
      forEach($compileNodes, function(node, index) {
6902
        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
6903
          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
6904
        }
6905
      });
6906
      var compositeLinkFn =
6907
              compileNodes($compileNodes, transcludeFn, $compileNodes,
6908
                           maxPriority, ignoreDirective, previousCompileContext);
6909
      compile.$$addScopeClass($compileNodes);
6910
      var namespace = null;
6911
      return function publicLinkFn(scope, cloneConnectFn, options) {
6912
        assertArg(scope, 'scope');
6913
 
6914
        options = options || {};
6915
        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
6916
          transcludeControllers = options.transcludeControllers,
6917
          futureParentElement = options.futureParentElement;
6918
 
6919
        // When `parentBoundTranscludeFn` is passed, it is a
6920
        // `controllersBoundTransclude` function (it was previously passed
6921
        // as `transclude` to directive.link) so we must unwrap it to get
6922
        // its `boundTranscludeFn`
6923
        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
6924
          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
6925
        }
6926
 
6927
        if (!namespace) {
6928
          namespace = detectNamespaceForChildElements(futureParentElement);
6929
        }
6930
        var $linkNode;
6931
        if (namespace !== 'html') {
6932
          // When using a directive with replace:true and templateUrl the $compileNodes
6933
          // (or a child element inside of them)
6934
          // might change, so we need to recreate the namespace adapted compileNodes
6935
          // for call to the link function.
6936
          // Note: This will already clone the nodes...
6937
          $linkNode = jqLite(
6938
            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
6939
          );
6940
        } else if (cloneConnectFn) {
6941
          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
6942
          // and sometimes changes the structure of the DOM.
6943
          $linkNode = JQLitePrototype.clone.call($compileNodes);
6944
        } else {
6945
          $linkNode = $compileNodes;
6946
        }
6947
 
6948
        if (transcludeControllers) {
6949
          for (var controllerName in transcludeControllers) {
6950
            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
6951
          }
6952
        }
6953
 
6954
        compile.$$addScopeInfo($linkNode, scope);
6955
 
6956
        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
6957
        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
6958
        return $linkNode;
6959
      };
6960
    }
6961
 
6962
    function detectNamespaceForChildElements(parentElement) {
6963
      // TODO: Make this detect MathML as well...
6964
      var node = parentElement && parentElement[0];
6965
      if (!node) {
6966
        return 'html';
6967
      } else {
6968
        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
6969
      }
6970
    }
6971
 
6972
    /**
6973
     * Compile function matches each node in nodeList against the directives. Once all directives
6974
     * for a particular node are collected their compile functions are executed. The compile
6975
     * functions return values - the linking functions - are combined into a composite linking
6976
     * function, which is the a linking function for the node.
6977
     *
6978
     * @param {NodeList} nodeList an array of nodes or NodeList to compile
6979
     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
6980
     *        scope argument is auto-generated to the new child of the transcluded parent scope.
6981
     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
6982
     *        the rootElement must be set the jqLite collection of the compile root. This is
6983
     *        needed so that the jqLite collection items can be replaced with widgets.
6984
     * @param {number=} maxPriority Max directive priority.
6985
     * @returns {Function} A composite linking function of all of the matched directives or null.
6986
     */
6987
    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
6988
                            previousCompileContext) {
6989
      var linkFns = [],
6990
          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
6991
 
6992
      for (var i = 0; i < nodeList.length; i++) {
6993
        attrs = new Attributes();
6994
 
6995
        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
6996
        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
6997
                                        ignoreDirective);
6998
 
6999
        nodeLinkFn = (directives.length)
7000
            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7001
                                      null, [], [], previousCompileContext)
7002
            : null;
7003
 
7004
        if (nodeLinkFn && nodeLinkFn.scope) {
7005
          compile.$$addScopeClass(attrs.$$element);
7006
        }
7007
 
7008
        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7009
                      !(childNodes = nodeList[i].childNodes) ||
7010
                      !childNodes.length)
7011
            ? null
7012
            : compileNodes(childNodes,
7013
                 nodeLinkFn ? (
7014
                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7015
                     && nodeLinkFn.transclude) : transcludeFn);
7016
 
7017
        if (nodeLinkFn || childLinkFn) {
7018
          linkFns.push(i, nodeLinkFn, childLinkFn);
7019
          linkFnFound = true;
7020
          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7021
        }
7022
 
7023
        //use the previous context only for the first element in the virtual group
7024
        previousCompileContext = null;
7025
      }
7026
 
7027
      // return a linking function if we have found anything, null otherwise
7028
      return linkFnFound ? compositeLinkFn : null;
7029
 
7030
      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7031
        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7032
        var stableNodeList;
7033
 
7034
 
7035
        if (nodeLinkFnFound) {
7036
          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7037
          // offsets don't get screwed up
7038
          var nodeListLength = nodeList.length;
7039
          stableNodeList = new Array(nodeListLength);
7040
 
7041
          // create a sparse array by only copying the elements which have a linkFn
7042
          for (i = 0; i < linkFns.length; i+=3) {
7043
            idx = linkFns[i];
7044
            stableNodeList[idx] = nodeList[idx];
7045
          }
7046
        } else {
7047
          stableNodeList = nodeList;
7048
        }
7049
 
7050
        for (i = 0, ii = linkFns.length; i < ii;) {
7051
          node = stableNodeList[linkFns[i++]];
7052
          nodeLinkFn = linkFns[i++];
7053
          childLinkFn = linkFns[i++];
7054
 
7055
          if (nodeLinkFn) {
7056
            if (nodeLinkFn.scope) {
7057
              childScope = scope.$new();
7058
              compile.$$addScopeInfo(jqLite(node), childScope);
7059
            } else {
7060
              childScope = scope;
7061
            }
7062
 
7063
            if (nodeLinkFn.transcludeOnThisElement) {
7064
              childBoundTranscludeFn = createBoundTranscludeFn(
7065
                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
7066
                  nodeLinkFn.elementTranscludeOnThisElement);
7067
 
7068
            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7069
              childBoundTranscludeFn = parentBoundTranscludeFn;
7070
 
7071
            } else if (!parentBoundTranscludeFn && transcludeFn) {
7072
              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7073
 
7074
            } else {
7075
              childBoundTranscludeFn = null;
7076
            }
7077
 
7078
            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7079
 
7080
          } else if (childLinkFn) {
7081
            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7082
          }
7083
        }
7084
      }
7085
    }
7086
 
7087
    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
7088
 
7089
      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7090
 
7091
        if (!transcludedScope) {
7092
          transcludedScope = scope.$new(false, containingScope);
7093
          transcludedScope.$$transcluded = true;
7094
        }
7095
 
7096
        return transcludeFn(transcludedScope, cloneFn, {
7097
          parentBoundTranscludeFn: previousBoundTranscludeFn,
7098
          transcludeControllers: controllers,
7099
          futureParentElement: futureParentElement
7100
        });
7101
      };
7102
 
7103
      return boundTranscludeFn;
7104
    }
7105
 
7106
    /**
7107
     * Looks for directives on the given node and adds them to the directive collection which is
7108
     * sorted.
7109
     *
7110
     * @param node Node to search.
7111
     * @param directives An array to which the directives are added to. This array is sorted before
7112
     *        the function returns.
7113
     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7114
     * @param {number=} maxPriority Max directive priority.
7115
     */
7116
    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7117
      var nodeType = node.nodeType,
7118
          attrsMap = attrs.$attr,
7119
          match,
7120
          className;
7121
 
7122
      switch (nodeType) {
7123
        case NODE_TYPE_ELEMENT: /* Element */
7124
          // use the node name: <directive>
7125
          addDirective(directives,
7126
              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7127
 
7128
          // iterate over the attributes
7129
          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7130
                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7131
            var attrStartName = false;
7132
            var attrEndName = false;
7133
 
7134
            attr = nAttrs[j];
7135
            name = attr.name;
7136
            value = trim(attr.value);
7137
 
7138
            // support ngAttr attribute binding
7139
            ngAttrName = directiveNormalize(name);
7140
            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7141
              name = name.replace(PREFIX_REGEXP, '')
7142
                .substr(8).replace(/_(.)/g, function(match, letter) {
7143
                  return letter.toUpperCase();
7144
                });
7145
            }
7146
 
7147
            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7148
            if (directiveIsMultiElement(directiveNName)) {
7149
              if (ngAttrName === directiveNName + 'Start') {
7150
                attrStartName = name;
7151
                attrEndName = name.substr(0, name.length - 5) + 'end';
7152
                name = name.substr(0, name.length - 6);
7153
              }
7154
            }
7155
 
7156
            nName = directiveNormalize(name.toLowerCase());
7157
            attrsMap[nName] = name;
7158
            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7159
                attrs[nName] = value;
7160
                if (getBooleanAttrName(node, nName)) {
7161
                  attrs[nName] = true; // presence means true
7162
                }
7163
            }
7164
            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7165
            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7166
                          attrEndName);
7167
          }
7168
 
7169
          // use class as directive
7170
          className = node.className;
7171
          if (isObject(className)) {
7172
              // Maybe SVGAnimatedString
7173
              className = className.animVal;
7174
          }
7175
          if (isString(className) && className !== '') {
7176
            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7177
              nName = directiveNormalize(match[2]);
7178
              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7179
                attrs[nName] = trim(match[3]);
7180
              }
7181
              className = className.substr(match.index + match[0].length);
7182
            }
7183
          }
7184
          break;
7185
        case NODE_TYPE_TEXT: /* Text Node */
7186
          addTextInterpolateDirective(directives, node.nodeValue);
7187
          break;
7188
        case NODE_TYPE_COMMENT: /* Comment */
7189
          try {
7190
            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7191
            if (match) {
7192
              nName = directiveNormalize(match[1]);
7193
              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7194
                attrs[nName] = trim(match[2]);
7195
              }
7196
            }
7197
          } catch (e) {
7198
            // turns out that under some circumstances IE9 throws errors when one attempts to read
7199
            // comment's node value.
7200
            // Just ignore it and continue. (Can't seem to reproduce in test case.)
7201
          }
7202
          break;
7203
      }
7204
 
7205
      directives.sort(byPriority);
7206
      return directives;
7207
    }
7208
 
7209
    /**
7210
     * Given a node with an directive-start it collects all of the siblings until it finds
7211
     * directive-end.
7212
     * @param node
7213
     * @param attrStart
7214
     * @param attrEnd
7215
     * @returns {*}
7216
     */
7217
    function groupScan(node, attrStart, attrEnd) {
7218
      var nodes = [];
7219
      var depth = 0;
7220
      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7221
        do {
7222
          if (!node) {
7223
            throw $compileMinErr('uterdir',
7224
                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7225
                      attrStart, attrEnd);
7226
          }
7227
          if (node.nodeType == NODE_TYPE_ELEMENT) {
7228
            if (node.hasAttribute(attrStart)) depth++;
7229
            if (node.hasAttribute(attrEnd)) depth--;
7230
          }
7231
          nodes.push(node);
7232
          node = node.nextSibling;
7233
        } while (depth > 0);
7234
      } else {
7235
        nodes.push(node);
7236
      }
7237
 
7238
      return jqLite(nodes);
7239
    }
7240
 
7241
    /**
7242
     * Wrapper for linking function which converts normal linking function into a grouped
7243
     * linking function.
7244
     * @param linkFn
7245
     * @param attrStart
7246
     * @param attrEnd
7247
     * @returns {Function}
7248
     */
7249
    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7250
      return function(scope, element, attrs, controllers, transcludeFn) {
7251
        element = groupScan(element[0], attrStart, attrEnd);
7252
        return linkFn(scope, element, attrs, controllers, transcludeFn);
7253
      };
7254
    }
7255
 
7256
    /**
7257
     * Once the directives have been collected, their compile functions are executed. This method
7258
     * is responsible for inlining directive templates as well as terminating the application
7259
     * of the directives if the terminal directive has been reached.
7260
     *
7261
     * @param {Array} directives Array of collected directives to execute their compile function.
7262
     *        this needs to be pre-sorted by priority order.
7263
     * @param {Node} compileNode The raw DOM node to apply the compile functions to
7264
     * @param {Object} templateAttrs The shared attribute function
7265
     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7266
     *                                                  scope argument is auto-generated to the new
7267
     *                                                  child of the transcluded parent scope.
7268
     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7269
     *                              argument has the root jqLite array so that we can replace nodes
7270
     *                              on it.
7271
     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7272
     *                                           compiling the transclusion.
7273
     * @param {Array.<Function>} preLinkFns
7274
     * @param {Array.<Function>} postLinkFns
7275
     * @param {Object} previousCompileContext Context used for previous compilation of the current
7276
     *                                        node
7277
     * @returns {Function} linkFn
7278
     */
7279
    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7280
                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7281
                                   previousCompileContext) {
7282
      previousCompileContext = previousCompileContext || {};
7283
 
7284
      var terminalPriority = -Number.MAX_VALUE,
7285
          newScopeDirective,
7286
          controllerDirectives = previousCompileContext.controllerDirectives,
7287
          controllers,
7288
          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7289
          templateDirective = previousCompileContext.templateDirective,
7290
          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7291
          hasTranscludeDirective = false,
7292
          hasTemplate = false,
7293
          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7294
          $compileNode = templateAttrs.$$element = jqLite(compileNode),
7295
          directive,
7296
          directiveName,
7297
          $template,
7298
          replaceDirective = originalReplaceDirective,
7299
          childTranscludeFn = transcludeFn,
7300
          linkFn,
7301
          directiveValue;
7302
 
7303
      // executes all directives on the current element
7304
      for (var i = 0, ii = directives.length; i < ii; i++) {
7305
        directive = directives[i];
7306
        var attrStart = directive.$$start;
7307
        var attrEnd = directive.$$end;
7308
 
7309
        // collect multiblock sections
7310
        if (attrStart) {
7311
          $compileNode = groupScan(compileNode, attrStart, attrEnd);
7312
        }
7313
        $template = undefined;
7314
 
7315
        if (terminalPriority > directive.priority) {
7316
          break; // prevent further processing of directives
7317
        }
7318
 
7319
        if (directiveValue = directive.scope) {
7320
 
7321
          // skip the check for directives with async templates, we'll check the derived sync
7322
          // directive when the template arrives
7323
          if (!directive.templateUrl) {
7324
            if (isObject(directiveValue)) {
7325
              // This directive is trying to add an isolated scope.
7326
              // Check that there is no scope of any kind already
7327
              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7328
                                directive, $compileNode);
7329
              newIsolateScopeDirective = directive;
7330
            } else {
7331
              // This directive is trying to add a child scope.
7332
              // Check that there is no isolated scope already
7333
              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7334
                                $compileNode);
7335
            }
7336
          }
7337
 
7338
          newScopeDirective = newScopeDirective || directive;
7339
        }
7340
 
7341
        directiveName = directive.name;
7342
 
7343
        if (!directive.templateUrl && directive.controller) {
7344
          directiveValue = directive.controller;
7345
          controllerDirectives = controllerDirectives || {};
7346
          assertNoDuplicate("'" + directiveName + "' controller",
7347
              controllerDirectives[directiveName], directive, $compileNode);
7348
          controllerDirectives[directiveName] = directive;
7349
        }
7350
 
7351
        if (directiveValue = directive.transclude) {
7352
          hasTranscludeDirective = true;
7353
 
7354
          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7355
          // This option should only be used by directives that know how to safely handle element transclusion,
7356
          // where the transcluded nodes are added or replaced after linking.
7357
          if (!directive.$$tlb) {
7358
            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7359
            nonTlbTranscludeDirective = directive;
7360
          }
7361
 
7362
          if (directiveValue == 'element') {
7363
            hasElementTranscludeDirective = true;
7364
            terminalPriority = directive.priority;
7365
            $template = $compileNode;
7366
            $compileNode = templateAttrs.$$element =
7367
                jqLite(document.createComment(' ' + directiveName + ': ' +
7368
                                              templateAttrs[directiveName] + ' '));
7369
            compileNode = $compileNode[0];
7370
            replaceWith(jqCollection, sliceArgs($template), compileNode);
7371
 
7372
            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7373
                                        replaceDirective && replaceDirective.name, {
7374
                                          // Don't pass in:
7375
                                          // - controllerDirectives - otherwise we'll create duplicates controllers
7376
                                          // - newIsolateScopeDirective or templateDirective - combining templates with
7377
                                          //   element transclusion doesn't make sense.
7378
                                          //
7379
                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7380
                                          // on the same element more than once.
7381
                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
7382
                                        });
7383
          } else {
7384
            $template = jqLite(jqLiteClone(compileNode)).contents();
7385
            $compileNode.empty(); // clear contents
7386
            childTranscludeFn = compile($template, transcludeFn);
7387
          }
7388
        }
7389
 
7390
        if (directive.template) {
7391
          hasTemplate = true;
7392
          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7393
          templateDirective = directive;
7394
 
7395
          directiveValue = (isFunction(directive.template))
7396
              ? directive.template($compileNode, templateAttrs)
7397
              : directive.template;
7398
 
7399
          directiveValue = denormalizeTemplate(directiveValue);
7400
 
7401
          if (directive.replace) {
7402
            replaceDirective = directive;
7403
            if (jqLiteIsTextNode(directiveValue)) {
7404
              $template = [];
7405
            } else {
7406
              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7407
            }
7408
            compileNode = $template[0];
7409
 
7410
            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7411
              throw $compileMinErr('tplrt',
7412
                  "Template for directive '{0}' must have exactly one root element. {1}",
7413
                  directiveName, '');
7414
            }
7415
 
7416
            replaceWith(jqCollection, $compileNode, compileNode);
7417
 
7418
            var newTemplateAttrs = {$attr: {}};
7419
 
7420
            // combine directives from the original node and from the template:
7421
            // - take the array of directives for this element
7422
            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7423
            // - collect directives from the template and sort them by priority
7424
            // - combine directives as: processed + template + unprocessed
7425
            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7426
            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7427
 
7428
            if (newIsolateScopeDirective) {
7429
              markDirectivesAsIsolate(templateDirectives);
7430
            }
7431
            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7432
            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7433
 
7434
            ii = directives.length;
7435
          } else {
7436
            $compileNode.html(directiveValue);
7437
          }
7438
        }
7439
 
7440
        if (directive.templateUrl) {
7441
          hasTemplate = true;
7442
          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7443
          templateDirective = directive;
7444
 
7445
          if (directive.replace) {
7446
            replaceDirective = directive;
7447
          }
7448
 
7449
          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7450
              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7451
                controllerDirectives: controllerDirectives,
7452
                newIsolateScopeDirective: newIsolateScopeDirective,
7453
                templateDirective: templateDirective,
7454
                nonTlbTranscludeDirective: nonTlbTranscludeDirective
7455
              });
7456
          ii = directives.length;
7457
        } else if (directive.compile) {
7458
          try {
7459
            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7460
            if (isFunction(linkFn)) {
7461
              addLinkFns(null, linkFn, attrStart, attrEnd);
7462
            } else if (linkFn) {
7463
              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7464
            }
7465
          } catch (e) {
7466
            $exceptionHandler(e, startingTag($compileNode));
7467
          }
7468
        }
7469
 
7470
        if (directive.terminal) {
7471
          nodeLinkFn.terminal = true;
7472
          terminalPriority = Math.max(terminalPriority, directive.priority);
7473
        }
7474
 
7475
      }
7476
 
7477
      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7478
      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7479
      nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
7480
      nodeLinkFn.templateOnThisElement = hasTemplate;
7481
      nodeLinkFn.transclude = childTranscludeFn;
7482
 
7483
      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7484
 
7485
      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7486
      return nodeLinkFn;
7487
 
7488
      ////////////////////
7489
 
7490
      function addLinkFns(pre, post, attrStart, attrEnd) {
7491
        if (pre) {
7492
          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7493
          pre.require = directive.require;
7494
          pre.directiveName = directiveName;
7495
          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7496
            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7497
          }
7498
          preLinkFns.push(pre);
7499
        }
7500
        if (post) {
7501
          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7502
          post.require = directive.require;
7503
          post.directiveName = directiveName;
7504
          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7505
            post = cloneAndAnnotateFn(post, {isolateScope: true});
7506
          }
7507
          postLinkFns.push(post);
7508
        }
7509
      }
7510
 
7511
 
7512
      function getControllers(directiveName, require, $element, elementControllers) {
7513
        var value, retrievalMethod = 'data', optional = false;
7514
        var $searchElement = $element;
7515
        var match;
7516
        if (isString(require)) {
7517
          match = require.match(REQUIRE_PREFIX_REGEXP);
7518
          require = require.substring(match[0].length);
7519
 
7520
          if (match[3]) {
7521
            if (match[1]) match[3] = null;
7522
            else match[1] = match[3];
7523
          }
7524
          if (match[1] === '^') {
7525
            retrievalMethod = 'inheritedData';
7526
          } else if (match[1] === '^^') {
7527
            retrievalMethod = 'inheritedData';
7528
            $searchElement = $element.parent();
7529
          }
7530
          if (match[2] === '?') {
7531
            optional = true;
7532
          }
7533
 
7534
          value = null;
7535
 
7536
          if (elementControllers && retrievalMethod === 'data') {
7537
            if (value = elementControllers[require]) {
7538
              value = value.instance;
7539
            }
7540
          }
7541
          value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
7542
 
7543
          if (!value && !optional) {
7544
            throw $compileMinErr('ctreq',
7545
                "Controller '{0}', required by directive '{1}', can't be found!",
7546
                require, directiveName);
7547
          }
7548
          return value || null;
7549
        } else if (isArray(require)) {
7550
          value = [];
7551
          forEach(require, function(require) {
7552
            value.push(getControllers(directiveName, require, $element, elementControllers));
7553
          });
7554
        }
7555
        return value;
7556
      }
7557
 
7558
 
7559
      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
7560
        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
7561
            attrs;
7562
 
7563
        if (compileNode === linkNode) {
7564
          attrs = templateAttrs;
7565
          $element = templateAttrs.$$element;
7566
        } else {
7567
          $element = jqLite(linkNode);
7568
          attrs = new Attributes($element, templateAttrs);
7569
        }
7570
 
7571
        if (newIsolateScopeDirective) {
7572
          isolateScope = scope.$new(true);
7573
        }
7574
 
7575
        if (boundTranscludeFn) {
7576
          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
7577
          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
7578
          transcludeFn = controllersBoundTransclude;
7579
          transcludeFn.$$boundTransclude = boundTranscludeFn;
7580
        }
7581
 
7582
        if (controllerDirectives) {
7583
          // TODO: merge `controllers` and `elementControllers` into single object.
7584
          controllers = {};
7585
          elementControllers = {};
7586
          forEach(controllerDirectives, function(directive) {
7587
            var locals = {
7588
              $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
7589
              $element: $element,
7590
              $attrs: attrs,
7591
              $transclude: transcludeFn
7592
            }, controllerInstance;
7593
 
7594
            controller = directive.controller;
7595
            if (controller == '@') {
7596
              controller = attrs[directive.name];
7597
            }
7598
 
7599
            controllerInstance = $controller(controller, locals, true, directive.controllerAs);
7600
 
7601
            // For directives with element transclusion the element is a comment,
7602
            // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
7603
            // clean up (http://bugs.jquery.com/ticket/8335).
7604
            // Instead, we save the controllers for the element in a local hash and attach to .data
7605
            // later, once we have the actual element.
7606
            elementControllers[directive.name] = controllerInstance;
7607
            if (!hasElementTranscludeDirective) {
7608
              $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
7609
            }
7610
 
7611
            controllers[directive.name] = controllerInstance;
7612
          });
7613
        }
7614
 
7615
        if (newIsolateScopeDirective) {
7616
          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
7617
              templateDirective === newIsolateScopeDirective.$$originalDirective)));
7618
          compile.$$addScopeClass($element, true);
7619
 
7620
          var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
7621
          var isolateBindingContext = isolateScope;
7622
          if (isolateScopeController && isolateScopeController.identifier &&
7623
              newIsolateScopeDirective.bindToController === true) {
7624
            isolateBindingContext = isolateScopeController.instance;
7625
          }
7626
 
7627
          forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
7628
            var attrName = definition.attrName,
7629
                optional = definition.optional,
7630
                mode = definition.mode, // @, =, or &
7631
                lastValue,
7632
                parentGet, parentSet, compare;
7633
 
7634
            switch (mode) {
7635
 
7636
              case '@':
7637
                attrs.$observe(attrName, function(value) {
7638
                  isolateBindingContext[scopeName] = value;
7639
                });
7640
                attrs.$$observers[attrName].$$scope = scope;
7641
                if (attrs[attrName]) {
7642
                  // If the attribute has been provided then we trigger an interpolation to ensure
7643
                  // the value is there for use in the link fn
7644
                  isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
7645
                }
7646
                break;
7647
 
7648
              case '=':
7649
                if (optional && !attrs[attrName]) {
7650
                  return;
7651
                }
7652
                parentGet = $parse(attrs[attrName]);
7653
                if (parentGet.literal) {
7654
                  compare = equals;
7655
                } else {
7656
                  compare = function(a, b) { return a === b || (a !== a && b !== b); };
7657
                }
7658
                parentSet = parentGet.assign || function() {
7659
                  // reset the change, or we will throw this exception on every $digest
7660
                  lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7661
                  throw $compileMinErr('nonassign',
7662
                      "Expression '{0}' used with directive '{1}' is non-assignable!",
7663
                      attrs[attrName], newIsolateScopeDirective.name);
7664
                };
7665
                lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7666
                var parentValueWatch = function parentValueWatch(parentValue) {
7667
                  if (!compare(parentValue, isolateBindingContext[scopeName])) {
7668
                    // we are out of sync and need to copy
7669
                    if (!compare(parentValue, lastValue)) {
7670
                      // parent changed and it has precedence
7671
                      isolateBindingContext[scopeName] = parentValue;
7672
                    } else {
7673
                      // if the parent can be assigned then do so
7674
                      parentSet(scope, parentValue = isolateBindingContext[scopeName]);
7675
                    }
7676
                  }
7677
                  return lastValue = parentValue;
7678
                };
7679
                parentValueWatch.$stateful = true;
7680
                var unwatch;
7681
                if (definition.collection) {
7682
                  unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
7683
                } else {
7684
                  unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
7685
                }
7686
                isolateScope.$on('$destroy', unwatch);
7687
                break;
7688
 
7689
              case '&':
7690
                parentGet = $parse(attrs[attrName]);
7691
                isolateBindingContext[scopeName] = function(locals) {
7692
                  return parentGet(scope, locals);
7693
                };
7694
                break;
7695
            }
7696
          });
7697
        }
7698
        if (controllers) {
7699
          forEach(controllers, function(controller) {
7700
            controller();
7701
          });
7702
          controllers = null;
7703
        }
7704
 
7705
        // PRELINKING
7706
        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
7707
          linkFn = preLinkFns[i];
7708
          invokeLinkFn(linkFn,
7709
              linkFn.isolateScope ? isolateScope : scope,
7710
              $element,
7711
              attrs,
7712
              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7713
              transcludeFn
7714
          );
7715
        }
7716
 
7717
        // RECURSION
7718
        // We only pass the isolate scope, if the isolate directive has a template,
7719
        // otherwise the child elements do not belong to the isolate directive.
7720
        var scopeToChild = scope;
7721
        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
7722
          scopeToChild = isolateScope;
7723
        }
7724
        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
7725
 
7726
        // POSTLINKING
7727
        for (i = postLinkFns.length - 1; i >= 0; i--) {
7728
          linkFn = postLinkFns[i];
7729
          invokeLinkFn(linkFn,
7730
              linkFn.isolateScope ? isolateScope : scope,
7731
              $element,
7732
              attrs,
7733
              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7734
              transcludeFn
7735
          );
7736
        }
7737
 
7738
        // This is the function that is injected as `$transclude`.
7739
        // Note: all arguments are optional!
7740
        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
7741
          var transcludeControllers;
7742
 
7743
          // No scope passed in:
7744
          if (!isScope(scope)) {
7745
            futureParentElement = cloneAttachFn;
7746
            cloneAttachFn = scope;
7747
            scope = undefined;
7748
          }
7749
 
7750
          if (hasElementTranscludeDirective) {
7751
            transcludeControllers = elementControllers;
7752
          }
7753
          if (!futureParentElement) {
7754
            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
7755
          }
7756
          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
7757
        }
7758
      }
7759
    }
7760
 
7761
    function markDirectivesAsIsolate(directives) {
7762
      // mark all directives as needing isolate scope.
7763
      for (var j = 0, jj = directives.length; j < jj; j++) {
7764
        directives[j] = inherit(directives[j], {$$isolateScope: true});
7765
      }
7766
    }
7767
 
7768
    /**
7769
     * looks up the directive and decorates it with exception handling and proper parameters. We
7770
     * call this the boundDirective.
7771
     *
7772
     * @param {string} name name of the directive to look up.
7773
     * @param {string} location The directive must be found in specific format.
7774
     *   String containing any of theses characters:
7775
     *
7776
     *   * `E`: element name
7777
     *   * `A': attribute
7778
     *   * `C`: class
7779
     *   * `M`: comment
7780
     * @returns {boolean} true if directive was added.
7781
     */
7782
    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
7783
                          endAttrName) {
7784
      if (name === ignoreDirective) return null;
7785
      var match = null;
7786
      if (hasDirectives.hasOwnProperty(name)) {
7787
        for (var directive, directives = $injector.get(name + Suffix),
7788
            i = 0, ii = directives.length; i < ii; i++) {
7789
          try {
7790
            directive = directives[i];
7791
            if ((maxPriority === undefined || maxPriority > directive.priority) &&
7792
                 directive.restrict.indexOf(location) != -1) {
7793
              if (startAttrName) {
7794
                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
7795
              }
7796
              tDirectives.push(directive);
7797
              match = directive;
7798
            }
7799
          } catch (e) { $exceptionHandler(e); }
7800
        }
7801
      }
7802
      return match;
7803
    }
7804
 
7805
 
7806
    /**
7807
     * looks up the directive and returns true if it is a multi-element directive,
7808
     * and therefore requires DOM nodes between -start and -end markers to be grouped
7809
     * together.
7810
     *
7811
     * @param {string} name name of the directive to look up.
7812
     * @returns true if directive was registered as multi-element.
7813
     */
7814
    function directiveIsMultiElement(name) {
7815
      if (hasDirectives.hasOwnProperty(name)) {
7816
        for (var directive, directives = $injector.get(name + Suffix),
7817
            i = 0, ii = directives.length; i < ii; i++) {
7818
          directive = directives[i];
7819
          if (directive.multiElement) {
7820
            return true;
7821
          }
7822
        }
7823
      }
7824
      return false;
7825
    }
7826
 
7827
    /**
7828
     * When the element is replaced with HTML template then the new attributes
7829
     * on the template need to be merged with the existing attributes in the DOM.
7830
     * The desired effect is to have both of the attributes present.
7831
     *
7832
     * @param {object} dst destination attributes (original DOM)
7833
     * @param {object} src source attributes (from the directive template)
7834
     */
7835
    function mergeTemplateAttributes(dst, src) {
7836
      var srcAttr = src.$attr,
7837
          dstAttr = dst.$attr,
7838
          $element = dst.$$element;
7839
 
7840
      // reapply the old attributes to the new element
7841
      forEach(dst, function(value, key) {
7842
        if (key.charAt(0) != '$') {
7843
          if (src[key] && src[key] !== value) {
7844
            value += (key === 'style' ? ';' : ' ') + src[key];
7845
          }
7846
          dst.$set(key, value, true, srcAttr[key]);
7847
        }
7848
      });
7849
 
7850
      // copy the new attributes on the old attrs object
7851
      forEach(src, function(value, key) {
7852
        if (key == 'class') {
7853
          safeAddClass($element, value);
7854
          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
7855
        } else if (key == 'style') {
7856
          $element.attr('style', $element.attr('style') + ';' + value);
7857
          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
7858
          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
7859
          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
7860
          // have an attribute like "has-own-property" or "data-has-own-property", etc.
7861
        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
7862
          dst[key] = value;
7863
          dstAttr[key] = srcAttr[key];
7864
        }
7865
      });
7866
    }
7867
 
7868
 
7869
    function compileTemplateUrl(directives, $compileNode, tAttrs,
7870
        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
7871
      var linkQueue = [],
7872
          afterTemplateNodeLinkFn,
7873
          afterTemplateChildLinkFn,
7874
          beforeTemplateCompileNode = $compileNode[0],
7875
          origAsyncDirective = directives.shift(),
7876
          derivedSyncDirective = inherit(origAsyncDirective, {
7877
            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
7878
          }),
7879
          templateUrl = (isFunction(origAsyncDirective.templateUrl))
7880
              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
7881
              : origAsyncDirective.templateUrl,
7882
          templateNamespace = origAsyncDirective.templateNamespace;
7883
 
7884
      $compileNode.empty();
7885
 
7886
      $templateRequest($sce.getTrustedResourceUrl(templateUrl))
7887
        .then(function(content) {
7888
          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
7889
 
7890
          content = denormalizeTemplate(content);
7891
 
7892
          if (origAsyncDirective.replace) {
7893
            if (jqLiteIsTextNode(content)) {
7894
              $template = [];
7895
            } else {
7896
              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
7897
            }
7898
            compileNode = $template[0];
7899
 
7900
            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7901
              throw $compileMinErr('tplrt',
7902
                  "Template for directive '{0}' must have exactly one root element. {1}",
7903
                  origAsyncDirective.name, templateUrl);
7904
            }
7905
 
7906
            tempTemplateAttrs = {$attr: {}};
7907
            replaceWith($rootElement, $compileNode, compileNode);
7908
            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
7909
 
7910
            if (isObject(origAsyncDirective.scope)) {
7911
              markDirectivesAsIsolate(templateDirectives);
7912
            }
7913
            directives = templateDirectives.concat(directives);
7914
            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
7915
          } else {
7916
            compileNode = beforeTemplateCompileNode;
7917
            $compileNode.html(content);
7918
          }
7919
 
7920
          directives.unshift(derivedSyncDirective);
7921
 
7922
          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
7923
              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
7924
              previousCompileContext);
7925
          forEach($rootElement, function(node, i) {
7926
            if (node == compileNode) {
7927
              $rootElement[i] = $compileNode[0];
7928
            }
7929
          });
7930
          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
7931
 
7932
          while (linkQueue.length) {
7933
            var scope = linkQueue.shift(),
7934
                beforeTemplateLinkNode = linkQueue.shift(),
7935
                linkRootElement = linkQueue.shift(),
7936
                boundTranscludeFn = linkQueue.shift(),
7937
                linkNode = $compileNode[0];
7938
 
7939
            if (scope.$$destroyed) continue;
7940
 
7941
            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
7942
              var oldClasses = beforeTemplateLinkNode.className;
7943
 
7944
              if (!(previousCompileContext.hasElementTranscludeDirective &&
7945
                  origAsyncDirective.replace)) {
7946
                // it was cloned therefore we have to clone as well.
7947
                linkNode = jqLiteClone(compileNode);
7948
              }
7949
              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
7950
 
7951
              // Copy in CSS classes from original node
7952
              safeAddClass(jqLite(linkNode), oldClasses);
7953
            }
7954
            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7955
              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7956
            } else {
7957
              childBoundTranscludeFn = boundTranscludeFn;
7958
            }
7959
            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
7960
              childBoundTranscludeFn);
7961
          }
7962
          linkQueue = null;
7963
        });
7964
 
7965
      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
7966
        var childBoundTranscludeFn = boundTranscludeFn;
7967
        if (scope.$$destroyed) return;
7968
        if (linkQueue) {
7969
          linkQueue.push(scope,
7970
                         node,
7971
                         rootElement,
7972
                         childBoundTranscludeFn);
7973
        } else {
7974
          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7975
            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7976
          }
7977
          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
7978
        }
7979
      };
7980
    }
7981
 
7982
 
7983
    /**
7984
     * Sorting function for bound directives.
7985
     */
7986
    function byPriority(a, b) {
7987
      var diff = b.priority - a.priority;
7988
      if (diff !== 0) return diff;
7989
      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
7990
      return a.index - b.index;
7991
    }
7992
 
7993
 
7994
    function assertNoDuplicate(what, previousDirective, directive, element) {
7995
      if (previousDirective) {
7996
        throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
7997
            previousDirective.name, directive.name, what, startingTag(element));
7998
      }
7999
    }
8000
 
8001
 
8002
    function addTextInterpolateDirective(directives, text) {
8003
      var interpolateFn = $interpolate(text, true);
8004
      if (interpolateFn) {
8005
        directives.push({
8006
          priority: 0,
8007
          compile: function textInterpolateCompileFn(templateNode) {
8008
            var templateNodeParent = templateNode.parent(),
8009
                hasCompileParent = !!templateNodeParent.length;
8010
 
8011
            // When transcluding a template that has bindings in the root
8012
            // we don't have a parent and thus need to add the class during linking fn.
8013
            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8014
 
8015
            return function textInterpolateLinkFn(scope, node) {
8016
              var parent = node.parent();
8017
              if (!hasCompileParent) compile.$$addBindingClass(parent);
8018
              compile.$$addBindingInfo(parent, interpolateFn.expressions);
8019
              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8020
                node[0].nodeValue = value;
8021
              });
8022
            };
8023
          }
8024
        });
8025
      }
8026
    }
8027
 
8028
 
8029
    function wrapTemplate(type, template) {
8030
      type = lowercase(type || 'html');
8031
      switch (type) {
8032
      case 'svg':
8033
      case 'math':
8034
        var wrapper = document.createElement('div');
8035
        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8036
        return wrapper.childNodes[0].childNodes;
8037
      default:
8038
        return template;
8039
      }
8040
    }
8041
 
8042
 
8043
    function getTrustedContext(node, attrNormalizedName) {
8044
      if (attrNormalizedName == "srcdoc") {
8045
        return $sce.HTML;
8046
      }
8047
      var tag = nodeName_(node);
8048
      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8049
      if (attrNormalizedName == "xlinkHref" ||
8050
          (tag == "form" && attrNormalizedName == "action") ||
8051
          (tag != "img" && (attrNormalizedName == "src" ||
8052
                            attrNormalizedName == "ngSrc"))) {
8053
        return $sce.RESOURCE_URL;
8054
      }
8055
    }
8056
 
8057
 
8058
    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8059
      var trustedContext = getTrustedContext(node, name);
8060
      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8061
 
8062
      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8063
 
8064
      // no interpolation found -> ignore
8065
      if (!interpolateFn) return;
8066
 
8067
 
8068
      if (name === "multiple" && nodeName_(node) === "select") {
8069
        throw $compileMinErr("selmulti",
8070
            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8071
            startingTag(node));
8072
      }
8073
 
8074
      directives.push({
8075
        priority: 100,
8076
        compile: function() {
8077
            return {
8078
              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8079
                var $$observers = (attr.$$observers || (attr.$$observers = {}));
8080
 
8081
                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8082
                  throw $compileMinErr('nodomevents',
8083
                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8084
                          "ng- versions (such as ng-click instead of onclick) instead.");
8085
                }
8086
 
8087
                // If the attribute has changed since last $interpolate()ed
8088
                var newValue = attr[name];
8089
                if (newValue !== value) {
8090
                  // we need to interpolate again since the attribute value has been updated
8091
                  // (e.g. by another directive's compile function)
8092
                  // ensure unset/empty values make interpolateFn falsy
8093
                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8094
                  value = newValue;
8095
                }
8096
 
8097
                // if attribute was updated so that there is no interpolation going on we don't want to
8098
                // register any observers
8099
                if (!interpolateFn) return;
8100
 
8101
                // initialize attr object so that it's ready in case we need the value for isolate
8102
                // scope initialization, otherwise the value would not be available from isolate
8103
                // directive's linking fn during linking phase
8104
                attr[name] = interpolateFn(scope);
8105
 
8106
                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8107
                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8108
                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8109
                    //special case for class attribute addition + removal
8110
                    //so that class changes can tap into the animation
8111
                    //hooks provided by the $animate service. Be sure to
8112
                    //skip animations when the first digest occurs (when
8113
                    //both the new and the old values are the same) since
8114
                    //the CSS classes are the non-interpolated values
8115
                    if (name === 'class' && newValue != oldValue) {
8116
                      attr.$updateClass(newValue, oldValue);
8117
                    } else {
8118
                      attr.$set(name, newValue);
8119
                    }
8120
                  });
8121
              }
8122
            };
8123
          }
8124
      });
8125
    }
8126
 
8127
 
8128
    /**
8129
     * This is a special jqLite.replaceWith, which can replace items which
8130
     * have no parents, provided that the containing jqLite collection is provided.
8131
     *
8132
     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8133
     *                               in the root of the tree.
8134
     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8135
     *                                  the shell, but replace its DOM node reference.
8136
     * @param {Node} newNode The new DOM node.
8137
     */
8138
    function replaceWith($rootElement, elementsToRemove, newNode) {
8139
      var firstElementToRemove = elementsToRemove[0],
8140
          removeCount = elementsToRemove.length,
8141
          parent = firstElementToRemove.parentNode,
8142
          i, ii;
8143
 
8144
      if ($rootElement) {
8145
        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8146
          if ($rootElement[i] == firstElementToRemove) {
8147
            $rootElement[i++] = newNode;
8148
            for (var j = i, j2 = j + removeCount - 1,
8149
                     jj = $rootElement.length;
8150
                 j < jj; j++, j2++) {
8151
              if (j2 < jj) {
8152
                $rootElement[j] = $rootElement[j2];
8153
              } else {
8154
                delete $rootElement[j];
8155
              }
8156
            }
8157
            $rootElement.length -= removeCount - 1;
8158
 
8159
            // If the replaced element is also the jQuery .context then replace it
8160
            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8161
            // http://api.jquery.com/context/
8162
            if ($rootElement.context === firstElementToRemove) {
8163
              $rootElement.context = newNode;
8164
            }
8165
            break;
8166
          }
8167
        }
8168
      }
8169
 
8170
      if (parent) {
8171
        parent.replaceChild(newNode, firstElementToRemove);
8172
      }
8173
 
8174
      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8175
      var fragment = document.createDocumentFragment();
8176
      fragment.appendChild(firstElementToRemove);
8177
 
8178
      // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8179
      // data here because there's no public interface in jQuery to do that and copying over
8180
      // event listeners (which is the main use of private data) wouldn't work anyway.
8181
      jqLite(newNode).data(jqLite(firstElementToRemove).data());
8182
 
8183
      // Remove data of the replaced element. We cannot just call .remove()
8184
      // on the element it since that would deallocate scope that is needed
8185
      // for the new node. Instead, remove the data "manually".
8186
      if (!jQuery) {
8187
        delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8188
      } else {
8189
        // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8190
        // the replaced element. The cleanData version monkey-patched by Angular would cause
8191
        // the scope to be trashed and we do need the very same scope to work with the new
8192
        // element. However, we cannot just cache the non-patched version and use it here as
8193
        // that would break if another library patches the method after Angular does (one
8194
        // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8195
        // skipped this one time.
8196
        skipDestroyOnNextJQueryCleanData = true;
8197
        jQuery.cleanData([firstElementToRemove]);
8198
      }
8199
 
8200
      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8201
        var element = elementsToRemove[k];
8202
        jqLite(element).remove(); // must do this way to clean up expando
8203
        fragment.appendChild(element);
8204
        delete elementsToRemove[k];
8205
      }
8206
 
8207
      elementsToRemove[0] = newNode;
8208
      elementsToRemove.length = 1;
8209
    }
8210
 
8211
 
8212
    function cloneAndAnnotateFn(fn, annotation) {
8213
      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8214
    }
8215
 
8216
 
8217
    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8218
      try {
8219
        linkFn(scope, $element, attrs, controllers, transcludeFn);
8220
      } catch (e) {
8221
        $exceptionHandler(e, startingTag($element));
8222
      }
8223
    }
8224
  }];
8225
}
8226
 
8227
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8228
/**
8229
 * Converts all accepted directives format into proper directive name.
8230
 * @param name Name to normalize
8231
 */
8232
function directiveNormalize(name) {
8233
  return camelCase(name.replace(PREFIX_REGEXP, ''));
8234
}
8235
 
8236
/**
8237
 * @ngdoc type
8238
 * @name $compile.directive.Attributes
8239
 *
8240
 * @description
8241
 * A shared object between directive compile / linking functions which contains normalized DOM
8242
 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8243
 * needed since all of these are treated as equivalent in Angular:
8244
 *
8245
 * ```
8246
 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8247
 * ```
8248
 */
8249
 
8250
/**
8251
 * @ngdoc property
8252
 * @name $compile.directive.Attributes#$attr
8253
 *
8254
 * @description
8255
 * A map of DOM element attribute names to the normalized name. This is
8256
 * needed to do reverse lookup from normalized name back to actual name.
8257
 */
8258
 
8259
 
8260
/**
8261
 * @ngdoc method
8262
 * @name $compile.directive.Attributes#$set
8263
 * @kind function
8264
 *
8265
 * @description
8266
 * Set DOM element attribute value.
8267
 *
8268
 *
8269
 * @param {string} name Normalized element attribute name of the property to modify. The name is
8270
 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8271
 *          property to the original name.
8272
 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8273
 */
8274
 
8275
 
8276
 
8277
/**
8278
 * Closure compiler type information
8279
 */
8280
 
8281
function nodesetLinkingFn(
8282
  /* angular.Scope */ scope,
8283
  /* NodeList */ nodeList,
8284
  /* Element */ rootElement,
8285
  /* function(Function) */ boundTranscludeFn
8286
) {}
8287
 
8288
function directiveLinkingFn(
8289
  /* nodesetLinkingFn */ nodesetLinkingFn,
8290
  /* angular.Scope */ scope,
8291
  /* Node */ node,
8292
  /* Element */ rootElement,
8293
  /* function(Function) */ boundTranscludeFn
8294
) {}
8295
 
8296
function tokenDifference(str1, str2) {
8297
  var values = '',
8298
      tokens1 = str1.split(/\s+/),
8299
      tokens2 = str2.split(/\s+/);
8300
 
8301
  outer:
8302
  for (var i = 0; i < tokens1.length; i++) {
8303
    var token = tokens1[i];
8304
    for (var j = 0; j < tokens2.length; j++) {
8305
      if (token == tokens2[j]) continue outer;
8306
    }
8307
    values += (values.length > 0 ? ' ' : '') + token;
8308
  }
8309
  return values;
8310
}
8311
 
8312
function removeComments(jqNodes) {
8313
  jqNodes = jqLite(jqNodes);
8314
  var i = jqNodes.length;
8315
 
8316
  if (i <= 1) {
8317
    return jqNodes;
8318
  }
8319
 
8320
  while (i--) {
8321
    var node = jqNodes[i];
8322
    if (node.nodeType === NODE_TYPE_COMMENT) {
8323
      splice.call(jqNodes, i, 1);
8324
    }
8325
  }
8326
  return jqNodes;
8327
}
8328
 
8329
var $controllerMinErr = minErr('$controller');
8330
 
8331
/**
8332
 * @ngdoc provider
8333
 * @name $controllerProvider
8334
 * @description
8335
 * The {@link ng.$controller $controller service} is used by Angular to create new
8336
 * controllers.
8337
 *
8338
 * This provider allows controller registration via the
8339
 * {@link ng.$controllerProvider#register register} method.
8340
 */
8341
function $ControllerProvider() {
8342
  var controllers = {},
8343
      globals = false,
8344
      CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8345
 
8346
 
8347
  /**
8348
   * @ngdoc method
8349
   * @name $controllerProvider#register
8350
   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8351
   *    the names and the values are the constructors.
8352
   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8353
   *    annotations in the array notation).
8354
   */
8355
  this.register = function(name, constructor) {
8356
    assertNotHasOwnProperty(name, 'controller');
8357
    if (isObject(name)) {
8358
      extend(controllers, name);
8359
    } else {
8360
      controllers[name] = constructor;
8361
    }
8362
  };
8363
 
8364
  /**
8365
   * @ngdoc method
8366
   * @name $controllerProvider#allowGlobals
8367
   * @description If called, allows `$controller` to find controller constructors on `window`
8368
   */
8369
  this.allowGlobals = function() {
8370
    globals = true;
8371
  };
8372
 
8373
 
8374
  this.$get = ['$injector', '$window', function($injector, $window) {
8375
 
8376
    /**
8377
     * @ngdoc service
8378
     * @name $controller
8379
     * @requires $injector
8380
     *
8381
     * @param {Function|string} constructor If called with a function then it's considered to be the
8382
     *    controller constructor function. Otherwise it's considered to be a string which is used
8383
     *    to retrieve the controller constructor using the following steps:
8384
     *
8385
     *    * check if a controller with given name is registered via `$controllerProvider`
8386
     *    * check if evaluating the string on the current scope returns a constructor
8387
     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8388
     *      `window` object (not recommended)
8389
     *
8390
     *    The string can use the `controller as property` syntax, where the controller instance is published
8391
     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8392
     *    to work correctly.
8393
     *
8394
     * @param {Object} locals Injection locals for Controller.
8395
     * @return {Object} Instance of given controller.
8396
     *
8397
     * @description
8398
     * `$controller` service is responsible for instantiating controllers.
8399
     *
8400
     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8401
     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8402
     */
8403
    return function(expression, locals, later, ident) {
8404
      // PRIVATE API:
8405
      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
8406
      //                     If true, $controller will allocate the object with the correct
8407
      //                     prototype chain, but will not invoke the controller until a returned
8408
      //                     callback is invoked.
8409
      //   param `ident` --- An optional label which overrides the label parsed from the controller
8410
      //                     expression, if any.
8411
      var instance, match, constructor, identifier;
8412
      later = later === true;
8413
      if (ident && isString(ident)) {
8414
        identifier = ident;
8415
      }
8416
 
8417
      if (isString(expression)) {
8418
        match = expression.match(CNTRL_REG);
8419
        if (!match) {
8420
          throw $controllerMinErr('ctrlfmt',
8421
            "Badly formed controller string '{0}'. " +
8422
            "Must match `__name__ as __id__` or `__name__`.", expression);
8423
        }
8424
        constructor = match[1],
8425
        identifier = identifier || match[3];
8426
        expression = controllers.hasOwnProperty(constructor)
8427
            ? controllers[constructor]
8428
            : getter(locals.$scope, constructor, true) ||
8429
                (globals ? getter($window, constructor, true) : undefined);
8430
 
8431
        assertArgFn(expression, constructor, true);
8432
      }
8433
 
8434
      if (later) {
8435
        // Instantiate controller later:
8436
        // This machinery is used to create an instance of the object before calling the
8437
        // controller's constructor itself.
8438
        //
8439
        // This allows properties to be added to the controller before the constructor is
8440
        // invoked. Primarily, this is used for isolate scope bindings in $compile.
8441
        //
8442
        // This feature is not intended for use by applications, and is thus not documented
8443
        // publicly.
8444
        // Object creation: http://jsperf.com/create-constructor/2
8445
        var controllerPrototype = (isArray(expression) ?
8446
          expression[expression.length - 1] : expression).prototype;
8447
        instance = Object.create(controllerPrototype || null);
8448
 
8449
        if (identifier) {
8450
          addIdentifier(locals, identifier, instance, constructor || expression.name);
8451
        }
8452
 
8453
        return extend(function() {
8454
          $injector.invoke(expression, instance, locals, constructor);
8455
          return instance;
8456
        }, {
8457
          instance: instance,
8458
          identifier: identifier
8459
        });
8460
      }
8461
 
8462
      instance = $injector.instantiate(expression, locals, constructor);
8463
 
8464
      if (identifier) {
8465
        addIdentifier(locals, identifier, instance, constructor || expression.name);
8466
      }
8467
 
8468
      return instance;
8469
    };
8470
 
8471
    function addIdentifier(locals, identifier, instance, name) {
8472
      if (!(locals && isObject(locals.$scope))) {
8473
        throw minErr('$controller')('noscp',
8474
          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
8475
          name, identifier);
8476
      }
8477
 
8478
      locals.$scope[identifier] = instance;
8479
    }
8480
  }];
8481
}
8482
 
8483
/**
8484
 * @ngdoc service
8485
 * @name $document
8486
 * @requires $window
8487
 *
8488
 * @description
8489
 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
8490
 *
8491
 * @example
8492
   <example module="documentExample">
8493
     <file name="index.html">
8494
       <div ng-controller="ExampleController">
8495
         <p>$document title: <b ng-bind="title"></b></p>
8496
         <p>window.document title: <b ng-bind="windowTitle"></b></p>
8497
       </div>
8498
     </file>
8499
     <file name="script.js">
8500
       angular.module('documentExample', [])
8501
         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
8502
           $scope.title = $document[0].title;
8503
           $scope.windowTitle = angular.element(window.document)[0].title;
8504
         }]);
8505
     </file>
8506
   </example>
8507
 */
8508
function $DocumentProvider() {
8509
  this.$get = ['$window', function(window) {
8510
    return jqLite(window.document);
8511
  }];
8512
}
8513
 
8514
/**
8515
 * @ngdoc service
8516
 * @name $exceptionHandler
8517
 * @requires ng.$log
8518
 *
8519
 * @description
8520
 * Any uncaught exception in angular expressions is delegated to this service.
8521
 * The default implementation simply delegates to `$log.error` which logs it into
8522
 * the browser console.
8523
 *
8524
 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
8525
 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
8526
 *
8527
 * ## Example:
8528
 *
8529
 * ```js
8530
 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
8531
 *     return function(exception, cause) {
8532
 *       exception.message += ' (caused by "' + cause + '")';
8533
 *       throw exception;
8534
 *     };
8535
 *   });
8536
 * ```
8537
 *
8538
 * This example will override the normal action of `$exceptionHandler`, to make angular
8539
 * exceptions fail hard when they happen, instead of just logging to the console.
8540
 *
8541
 * <hr />
8542
 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
8543
 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
8544
 * (unless executed during a digest).
8545
 *
8546
 * If you wish, you can manually delegate exceptions, e.g.
8547
 * `try { ... } catch(e) { $exceptionHandler(e); }`
8548
 *
8549
 * @param {Error} exception Exception associated with the error.
8550
 * @param {string=} cause optional information about the context in which
8551
 *       the error was thrown.
8552
 *
8553
 */
8554
function $ExceptionHandlerProvider() {
8555
  this.$get = ['$log', function($log) {
8556
    return function(exception, cause) {
8557
      $log.error.apply($log, arguments);
8558
    };
8559
  }];
8560
}
8561
 
8562
var APPLICATION_JSON = 'application/json';
8563
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
8564
var JSON_START = /^\[|^\{(?!\{)/;
8565
var JSON_ENDS = {
8566
  '[': /]$/,
8567
  '{': /}$/
8568
};
8569
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
8570
 
8571
function defaultHttpResponseTransform(data, headers) {
8572
  if (isString(data)) {
8573
    // Strip json vulnerability protection prefix and trim whitespace
8574
    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
8575
 
8576
    if (tempData) {
8577
      var contentType = headers('Content-Type');
8578
      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
8579
        data = fromJson(tempData);
8580
      }
8581
    }
8582
  }
8583
 
8584
  return data;
8585
}
8586
 
8587
function isJsonLike(str) {
8588
    var jsonStart = str.match(JSON_START);
8589
    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
8590
}
8591
 
8592
/**
8593
 * Parse headers into key value object
8594
 *
8595
 * @param {string} headers Raw headers as a string
8596
 * @returns {Object} Parsed headers as key value object
8597
 */
8598
function parseHeaders(headers) {
8599
  var parsed = createMap(), key, val, i;
8600
 
8601
  if (!headers) return parsed;
8602
 
8603
  forEach(headers.split('\n'), function(line) {
8604
    i = line.indexOf(':');
8605
    key = lowercase(trim(line.substr(0, i)));
8606
    val = trim(line.substr(i + 1));
8607
 
8608
    if (key) {
8609
      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
8610
    }
8611
  });
8612
 
8613
  return parsed;
8614
}
8615
 
8616
 
8617
/**
8618
 * Returns a function that provides access to parsed headers.
8619
 *
8620
 * Headers are lazy parsed when first requested.
8621
 * @see parseHeaders
8622
 *
8623
 * @param {(string|Object)} headers Headers to provide access to.
8624
 * @returns {function(string=)} Returns a getter function which if called with:
8625
 *
8626
 *   - if called with single an argument returns a single header value or null
8627
 *   - if called with no arguments returns an object containing all headers.
8628
 */
8629
function headersGetter(headers) {
8630
  var headersObj = isObject(headers) ? headers : undefined;
8631
 
8632
  return function(name) {
8633
    if (!headersObj) headersObj =  parseHeaders(headers);
8634
 
8635
    if (name) {
8636
      var value = headersObj[lowercase(name)];
8637
      if (value === void 0) {
8638
        value = null;
8639
      }
8640
      return value;
8641
    }
8642
 
8643
    return headersObj;
8644
  };
8645
}
8646
 
8647
 
8648
/**
8649
 * Chain all given functions
8650
 *
8651
 * This function is used for both request and response transforming
8652
 *
8653
 * @param {*} data Data to transform.
8654
 * @param {function(string=)} headers HTTP headers getter fn.
8655
 * @param {number} status HTTP status code of the response.
8656
 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
8657
 * @returns {*} Transformed data.
8658
 */
8659
function transformData(data, headers, status, fns) {
8660
  if (isFunction(fns))
8661
    return fns(data, headers, status);
8662
 
8663
  forEach(fns, function(fn) {
8664
    data = fn(data, headers, status);
8665
  });
8666
 
8667
  return data;
8668
}
8669
 
8670
 
8671
function isSuccess(status) {
8672
  return 200 <= status && status < 300;
8673
}
8674
 
8675
 
8676
/**
8677
 * @ngdoc provider
8678
 * @name $httpProvider
8679
 * @description
8680
 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
8681
 * */
8682
function $HttpProvider() {
8683
  /**
8684
   * @ngdoc property
8685
   * @name $httpProvider#defaults
8686
   * @description
8687
   *
8688
   * Object containing default values for all {@link ng.$http $http} requests.
8689
   *
8690
   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8691
   * that will provide the cache for all requests who set their `cache` property to `true`.
8692
   * If you set the `default.cache = false` then only requests that specify their own custom
8693
   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8694
   *
8695
   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8696
   * Defaults value is `'XSRF-TOKEN'`.
8697
   *
8698
   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
8699
   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
8700
   *
8701
   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
8702
   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
8703
   * setting default headers.
8704
   *     - **`defaults.headers.common`**
8705
   *     - **`defaults.headers.post`**
8706
   *     - **`defaults.headers.put`**
8707
   *     - **`defaults.headers.patch`**
8708
   *
8709
   **/
8710
  var defaults = this.defaults = {
8711
    // transform incoming response data
8712
    transformResponse: [defaultHttpResponseTransform],
8713
 
8714
    // transform outgoing request data
8715
    transformRequest: [function(d) {
8716
      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
8717
    }],
8718
 
8719
    // default headers
8720
    headers: {
8721
      common: {
8722
        'Accept': 'application/json, text/plain, */*'
8723
      },
8724
      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8725
      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8726
      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
8727
    },
8728
 
8729
    xsrfCookieName: 'XSRF-TOKEN',
8730
    xsrfHeaderName: 'X-XSRF-TOKEN'
8731
  };
8732
 
8733
  var useApplyAsync = false;
8734
  /**
8735
   * @ngdoc method
8736
   * @name $httpProvider#useApplyAsync
8737
   * @description
8738
   *
8739
   * Configure $http service to combine processing of multiple http responses received at around
8740
   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
8741
   * significant performance improvement for bigger applications that make many HTTP requests
8742
   * concurrently (common during application bootstrap).
8743
   *
8744
   * Defaults to false. If no value is specifed, returns the current configured value.
8745
   *
8746
   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
8747
   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
8748
   *    to load and share the same digest cycle.
8749
   *
8750
   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
8751
   *    otherwise, returns the current configured value.
8752
   **/
8753
  this.useApplyAsync = function(value) {
8754
    if (isDefined(value)) {
8755
      useApplyAsync = !!value;
8756
      return this;
8757
    }
8758
    return useApplyAsync;
8759
  };
8760
 
8761
  /**
8762
   * @ngdoc property
8763
   * @name $httpProvider#interceptors
8764
   * @description
8765
   *
8766
   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
8767
   * pre-processing of request or postprocessing of responses.
8768
   *
8769
   * These service factories are ordered by request, i.e. they are applied in the same order as the
8770
   * array, on request, but reverse order, on response.
8771
   *
8772
   * {@link ng.$http#interceptors Interceptors detailed info}
8773
   **/
8774
  var interceptorFactories = this.interceptors = [];
8775
 
8776
  this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
8777
      function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
8778
 
8779
    var defaultCache = $cacheFactory('$http');
8780
 
8781
    /**
8782
     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
8783
     * The reversal is needed so that we can build up the interception chain around the
8784
     * server request.
8785
     */
8786
    var reversedInterceptors = [];
8787
 
8788
    forEach(interceptorFactories, function(interceptorFactory) {
8789
      reversedInterceptors.unshift(isString(interceptorFactory)
8790
          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
8791
    });
8792
 
8793
    /**
8794
     * @ngdoc service
8795
     * @kind function
8796
     * @name $http
8797
     * @requires ng.$httpBackend
8798
     * @requires $cacheFactory
8799
     * @requires $rootScope
8800
     * @requires $q
8801
     * @requires $injector
8802
     *
8803
     * @description
8804
     * The `$http` service is a core Angular service that facilitates communication with the remote
8805
     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
8806
     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
8807
     *
8808
     * For unit testing applications that use `$http` service, see
8809
     * {@link ngMock.$httpBackend $httpBackend mock}.
8810
     *
8811
     * For a higher level of abstraction, please check out the {@link ngResource.$resource
8812
     * $resource} service.
8813
     *
8814
     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8815
     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8816
     * it is important to familiarize yourself with these APIs and the guarantees they provide.
8817
     *
8818
     *
8819
     * ## General usage
8820
     * The `$http` service is a function which takes a single argument — a configuration object —
8821
     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
8822
     * with two $http specific methods: `success` and `error`.
8823
     *
8824
     * ```js
8825
     *   // Simple GET request example :
8826
     *   $http.get('/someUrl').
8827
     *     success(function(data, status, headers, config) {
8828
     *       // this callback will be called asynchronously
8829
     *       // when the response is available
8830
     *     }).
8831
     *     error(function(data, status, headers, config) {
8832
     *       // called asynchronously if an error occurs
8833
     *       // or server returns response with an error status.
8834
     *     });
8835
     * ```
8836
     *
8837
     * ```js
8838
     *   // Simple POST request example (passing data) :
8839
     *   $http.post('/someUrl', {msg:'hello word!'}).
8840
     *     success(function(data, status, headers, config) {
8841
     *       // this callback will be called asynchronously
8842
     *       // when the response is available
8843
     *     }).
8844
     *     error(function(data, status, headers, config) {
8845
     *       // called asynchronously if an error occurs
8846
     *       // or server returns response with an error status.
8847
     *     });
8848
     * ```
8849
     *
8850
     *
8851
     * Since the returned value of calling the $http function is a `promise`, you can also use
8852
     * the `then` method to register callbacks, and these callbacks will receive a single argument –
8853
     * an object representing the response. See the API signature and type info below for more
8854
     * details.
8855
     *
8856
     * A response status code between 200 and 299 is considered a success status and
8857
     * will result in the success callback being called. Note that if the response is a redirect,
8858
     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8859
     * called for such responses.
8860
     *
8861
     * ## Writing Unit Tests that use $http
8862
     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
8863
     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
8864
     * request using trained responses.
8865
     *
8866
     * ```
8867
     * $httpBackend.expectGET(...);
8868
     * $http.get(...);
8869
     * $httpBackend.flush();
8870
     * ```
8871
     *
8872
     * ## Shortcut methods
8873
     *
8874
     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
8875
     * request data must be passed in for POST/PUT requests.
8876
     *
8877
     * ```js
8878
     *   $http.get('/someUrl').success(successCallback);
8879
     *   $http.post('/someUrl', data).success(successCallback);
8880
     * ```
8881
     *
8882
     * Complete list of shortcut methods:
8883
     *
8884
     * - {@link ng.$http#get $http.get}
8885
     * - {@link ng.$http#head $http.head}
8886
     * - {@link ng.$http#post $http.post}
8887
     * - {@link ng.$http#put $http.put}
8888
     * - {@link ng.$http#delete $http.delete}
8889
     * - {@link ng.$http#jsonp $http.jsonp}
8890
     * - {@link ng.$http#patch $http.patch}
8891
     *
8892
     *
8893
     * ## Setting HTTP Headers
8894
     *
8895
     * The $http service will automatically add certain HTTP headers to all requests. These defaults
8896
     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8897
     * object, which currently contains this default configuration:
8898
     *
8899
     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8900
     *   - `Accept: application/json, text/plain, * / *`
8901
     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8902
     *   - `Content-Type: application/json`
8903
     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8904
     *   - `Content-Type: application/json`
8905
     *
8906
     * To add or overwrite these defaults, simply add or remove a property from these configuration
8907
     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8908
     * with the lowercased HTTP method name as the key, e.g.
8909
     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
8910
     *
8911
     * The defaults can also be set at runtime via the `$http.defaults` object in the same
8912
     * fashion. For example:
8913
     *
8914
     * ```
8915
     * module.run(function($http) {
8916
     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
8917
     * });
8918
     * ```
8919
     *
8920
     * In addition, you can supply a `headers` property in the config object passed when
8921
     * calling `$http(config)`, which overrides the defaults without changing them globally.
8922
     *
8923
     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8924
     * Use the `headers` property, setting the desired header to `undefined`. For example:
8925
     *
8926
     * ```js
8927
     * var req = {
8928
     *  method: 'POST',
8929
     *  url: 'http://example.com',
8930
     *  headers: {
8931
     *    'Content-Type': undefined
8932
     *  },
8933
     *  data: { test: 'test' },
8934
     * }
8935
     *
8936
     * $http(req).success(function(){...}).error(function(){...});
8937
     * ```
8938
     *
8939
     * ## Transforming Requests and Responses
8940
     *
8941
     * Both requests and responses can be transformed using transformation functions: `transformRequest`
8942
     * and `transformResponse`. These properties can be a single function that returns
8943
     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
8944
     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
8945
     *
8946
     * ### Default Transformations
8947
     *
8948
     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
8949
     * `defaults.transformResponse` properties. If a request does not provide its own transformations
8950
     * then these will be applied.
8951
     *
8952
     * You can augment or replace the default transformations by modifying these properties by adding to or
8953
     * replacing the array.
8954
     *
8955
     * Angular provides the following default transformations:
8956
     *
8957
     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
8958
     *
8959
     * - If the `data` property of the request configuration object contains an object, serialize it
8960
     *   into JSON format.
8961
     *
8962
     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
8963
     *
8964
     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
8965
     *  - If JSON response is detected, deserialize it using a JSON parser.
8966
     *
8967
     *
8968
     * ### Overriding the Default Transformations Per Request
8969
     *
8970
     * If you wish override the request/response transformations only for a single request then provide
8971
     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
8972
     * into `$http`.
8973
     *
8974
     * Note that if you provide these properties on the config object the default transformations will be
8975
     * overwritten. If you wish to augment the default transformations then you must include them in your
8976
     * local transformation array.
8977
     *
8978
     * The following code demonstrates adding a new response transformation to be run after the default response
8979
     * transformations have been run.
8980
     *
8981
     * ```js
8982
     * function appendTransform(defaults, transform) {
8983
     *
8984
     *   // We can't guarantee that the default transformation is an array
8985
     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
8986
     *
8987
     *   // Append the new transformation to the defaults
8988
     *   return defaults.concat(transform);
8989
     * }
8990
     *
8991
     * $http({
8992
     *   url: '...',
8993
     *   method: 'GET',
8994
     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
8995
     *     return doTransform(value);
8996
     *   })
8997
     * });
8998
     * ```
8999
     *
9000
     *
9001
     * ## Caching
9002
     *
9003
     * To enable caching, set the request configuration `cache` property to `true` (to use default
9004
     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9005
     * When the cache is enabled, `$http` stores the response from the server in the specified
9006
     * cache. The next time the same request is made, the response is served from the cache without
9007
     * sending a request to the server.
9008
     *
9009
     * Note that even if the response is served from cache, delivery of the data is asynchronous in
9010
     * the same way that real requests are.
9011
     *
9012
     * If there are multiple GET requests for the same URL that should be cached using the same
9013
     * cache, but the cache is not populated yet, only one request to the server will be made and
9014
     * the remaining requests will be fulfilled using the response from the first request.
9015
     *
9016
     * You can change the default cache to a new object (built with
9017
     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9018
     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9019
     * their `cache` property to `true` will now use this cache object.
9020
     *
9021
     * If you set the default cache to `false` then only requests that specify their own custom
9022
     * cache object will be cached.
9023
     *
9024
     * ## Interceptors
9025
     *
9026
     * Before you start creating interceptors, be sure to understand the
9027
     * {@link ng.$q $q and deferred/promise APIs}.
9028
     *
9029
     * For purposes of global error handling, authentication, or any kind of synchronous or
9030
     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9031
     * able to intercept requests before they are handed to the server and
9032
     * responses before they are handed over to the application code that
9033
     * initiated these requests. The interceptors leverage the {@link ng.$q
9034
     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9035
     *
9036
     * The interceptors are service factories that are registered with the `$httpProvider` by
9037
     * adding them to the `$httpProvider.interceptors` array. The factory is called and
9038
     * injected with dependencies (if specified) and returns the interceptor.
9039
     *
9040
     * There are two kinds of interceptors (and two kinds of rejection interceptors):
9041
     *
9042
     *   * `request`: interceptors get called with a http `config` object. The function is free to
9043
     *     modify the `config` object or create a new one. The function needs to return the `config`
9044
     *     object directly, or a promise containing the `config` or a new `config` object.
9045
     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
9046
     *     resolved with a rejection.
9047
     *   * `response`: interceptors get called with http `response` object. The function is free to
9048
     *     modify the `response` object or create a new one. The function needs to return the `response`
9049
     *     object directly, or as a promise containing the `response` or a new `response` object.
9050
     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
9051
     *     resolved with a rejection.
9052
     *
9053
     *
9054
     * ```js
9055
     *   // register the interceptor as a service
9056
     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9057
     *     return {
9058
     *       // optional method
9059
     *       'request': function(config) {
9060
     *         // do something on success
9061
     *         return config;
9062
     *       },
9063
     *
9064
     *       // optional method
9065
     *      'requestError': function(rejection) {
9066
     *         // do something on error
9067
     *         if (canRecover(rejection)) {
9068
     *           return responseOrNewPromise
9069
     *         }
9070
     *         return $q.reject(rejection);
9071
     *       },
9072
     *
9073
     *
9074
     *
9075
     *       // optional method
9076
     *       'response': function(response) {
9077
     *         // do something on success
9078
     *         return response;
9079
     *       },
9080
     *
9081
     *       // optional method
9082
     *      'responseError': function(rejection) {
9083
     *         // do something on error
9084
     *         if (canRecover(rejection)) {
9085
     *           return responseOrNewPromise
9086
     *         }
9087
     *         return $q.reject(rejection);
9088
     *       }
9089
     *     };
9090
     *   });
9091
     *
9092
     *   $httpProvider.interceptors.push('myHttpInterceptor');
9093
     *
9094
     *
9095
     *   // alternatively, register the interceptor via an anonymous factory
9096
     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9097
     *     return {
9098
     *      'request': function(config) {
9099
     *          // same as above
9100
     *       },
9101
     *
9102
     *       'response': function(response) {
9103
     *          // same as above
9104
     *       }
9105
     *     };
9106
     *   });
9107
     * ```
9108
     *
9109
     * ## Security Considerations
9110
     *
9111
     * When designing web applications, consider security threats from:
9112
     *
9113
     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9114
     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9115
     *
9116
     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9117
     * pre-configured with strategies that address these issues, but for this to work backend server
9118
     * cooperation is required.
9119
     *
9120
     * ### JSON Vulnerability Protection
9121
     *
9122
     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9123
     * allows third party website to turn your JSON resource URL into
9124
     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9125
     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9126
     * Angular will automatically strip the prefix before processing it as JSON.
9127
     *
9128
     * For example if your server needs to return:
9129
     * ```js
9130
     * ['one','two']
9131
     * ```
9132
     *
9133
     * which is vulnerable to attack, your server can return:
9134
     * ```js
9135
     * )]}',
9136
     * ['one','two']
9137
     * ```
9138
     *
9139
     * Angular will strip the prefix, before processing the JSON.
9140
     *
9141
     *
9142
     * ### Cross Site Request Forgery (XSRF) Protection
9143
     *
9144
     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9145
     * an unauthorized site can gain your user's private data. Angular provides a mechanism
9146
     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9147
     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9148
     * JavaScript that runs on your domain could read the cookie, your server can be assured that
9149
     * the XHR came from JavaScript running on your domain. The header will not be set for
9150
     * cross-domain requests.
9151
     *
9152
     * To take advantage of this, your server needs to set a token in a JavaScript readable session
9153
     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9154
     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9155
     * that only JavaScript running on your domain could have sent the request. The token must be
9156
     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9157
     * making up its own tokens). We recommend that the token is a digest of your site's
9158
     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
9159
     * for added security.
9160
     *
9161
     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9162
     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9163
     * or the per-request config object.
9164
     *
9165
     *
9166
     * @param {object} config Object describing the request to be made and how it should be
9167
     *    processed. The object has following properties:
9168
     *
9169
     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9170
     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9171
     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
9172
     *      to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
9173
     *      JSONified.
9174
     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
9175
     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
9176
     *      HTTP headers to send to the server. If the return value of a function is null, the
9177
     *      header will not be sent.
9178
     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9179
     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9180
     *    - **transformRequest** –
9181
     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9182
     *      transform function or an array of such functions. The transform function takes the http
9183
     *      request body and headers and returns its transformed (typically serialized) version.
9184
     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9185
     *      Overriding the Default Transformations}
9186
     *    - **transformResponse** –
9187
     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
9188
     *      transform function or an array of such functions. The transform function takes the http
9189
     *      response body, headers and status and returns its transformed (typically deserialized) version.
9190
     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9191
     *      Overriding the Default Transformations}
9192
     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9193
     *      GET request, otherwise if a cache instance built with
9194
     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9195
     *      caching.
9196
     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9197
     *      that should abort the request when resolved.
9198
     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9199
     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9200
     *      for more information.
9201
     *    - **responseType** - `{string}` - see
9202
     *      [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9203
     *
9204
     * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9205
     *   standard `then` method and two http specific methods: `success` and `error`. The `then`
9206
     *   method takes two arguments a success and an error callback which will be called with a
9207
     *   response object. The `success` and `error` methods take a single argument - a function that
9208
     *   will be called when the request succeeds or fails respectively. The arguments passed into
9209
     *   these functions are destructured representation of the response object passed into the
9210
     *   `then` method. The response object has these properties:
9211
     *
9212
     *   - **data** – `{string|Object}` – The response body transformed with the transform
9213
     *     functions.
9214
     *   - **status** – `{number}` – HTTP status code of the response.
9215
     *   - **headers** – `{function([headerName])}` – Header getter function.
9216
     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9217
     *   - **statusText** – `{string}` – HTTP status text of the response.
9218
     *
9219
     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9220
     *   requests. This is primarily meant to be used for debugging purposes.
9221
     *
9222
     *
9223
     * @example
9224
<example module="httpExample">
9225
<file name="index.html">
9226
  <div ng-controller="FetchController">
9227
    <select ng-model="method">
9228
      <option>GET</option>
9229
      <option>JSONP</option>
9230
    </select>
9231
    <input type="text" ng-model="url" size="80"/>
9232
    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9233
    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9234
    <button id="samplejsonpbtn"
9235
      ng-click="updateModel('JSONP',
9236
                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9237
      Sample JSONP
9238
    </button>
9239
    <button id="invalidjsonpbtn"
9240
      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9241
        Invalid JSONP
9242
      </button>
9243
    <pre>http status code: {{status}}</pre>
9244
    <pre>http response data: {{data}}</pre>
9245
  </div>
9246
</file>
9247
<file name="script.js">
9248
  angular.module('httpExample', [])
9249
    .controller('FetchController', ['$scope', '$http', '$templateCache',
9250
      function($scope, $http, $templateCache) {
9251
        $scope.method = 'GET';
9252
        $scope.url = 'http-hello.html';
9253
 
9254
        $scope.fetch = function() {
9255
          $scope.code = null;
9256
          $scope.response = null;
9257
 
9258
          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9259
            success(function(data, status) {
9260
              $scope.status = status;
9261
              $scope.data = data;
9262
            }).
9263
            error(function(data, status) {
9264
              $scope.data = data || "Request failed";
9265
              $scope.status = status;
9266
          });
9267
        };
9268
 
9269
        $scope.updateModel = function(method, url) {
9270
          $scope.method = method;
9271
          $scope.url = url;
9272
        };
9273
      }]);
9274
</file>
9275
<file name="http-hello.html">
9276
  Hello, $http!
9277
</file>
9278
<file name="protractor.js" type="protractor">
9279
  var status = element(by.binding('status'));
9280
  var data = element(by.binding('data'));
9281
  var fetchBtn = element(by.id('fetchbtn'));
9282
  var sampleGetBtn = element(by.id('samplegetbtn'));
9283
  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9284
  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9285
 
9286
  it('should make an xhr GET request', function() {
9287
    sampleGetBtn.click();
9288
    fetchBtn.click();
9289
    expect(status.getText()).toMatch('200');
9290
    expect(data.getText()).toMatch(/Hello, \$http!/);
9291
  });
9292
 
9293
// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9294
// it('should make a JSONP request to angularjs.org', function() {
9295
//   sampleJsonpBtn.click();
9296
//   fetchBtn.click();
9297
//   expect(status.getText()).toMatch('200');
9298
//   expect(data.getText()).toMatch(/Super Hero!/);
9299
// });
9300
 
9301
  it('should make JSONP request to invalid URL and invoke the error handler',
9302
      function() {
9303
    invalidJsonpBtn.click();
9304
    fetchBtn.click();
9305
    expect(status.getText()).toMatch('0');
9306
    expect(data.getText()).toMatch('Request failed');
9307
  });
9308
</file>
9309
</example>
9310
     */
9311
    function $http(requestConfig) {
9312
 
9313
      if (!angular.isObject(requestConfig)) {
9314
        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
9315
      }
9316
 
9317
      var config = extend({
9318
        method: 'get',
9319
        transformRequest: defaults.transformRequest,
9320
        transformResponse: defaults.transformResponse
9321
      }, requestConfig);
9322
 
9323
      config.headers = mergeHeaders(requestConfig);
9324
      config.method = uppercase(config.method);
9325
 
9326
      var serverRequest = function(config) {
9327
        var headers = config.headers;
9328
        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
9329
 
9330
        // strip content-type if data is undefined
9331
        if (isUndefined(reqData)) {
9332
          forEach(headers, function(value, header) {
9333
            if (lowercase(header) === 'content-type') {
9334
                delete headers[header];
9335
            }
9336
          });
9337
        }
9338
 
9339
        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9340
          config.withCredentials = defaults.withCredentials;
9341
        }
9342
 
9343
        // send request
9344
        return sendReq(config, reqData).then(transformResponse, transformResponse);
9345
      };
9346
 
9347
      var chain = [serverRequest, undefined];
9348
      var promise = $q.when(config);
9349
 
9350
      // apply interceptors
9351
      forEach(reversedInterceptors, function(interceptor) {
9352
        if (interceptor.request || interceptor.requestError) {
9353
          chain.unshift(interceptor.request, interceptor.requestError);
9354
        }
9355
        if (interceptor.response || interceptor.responseError) {
9356
          chain.push(interceptor.response, interceptor.responseError);
9357
        }
9358
      });
9359
 
9360
      while (chain.length) {
9361
        var thenFn = chain.shift();
9362
        var rejectFn = chain.shift();
9363
 
9364
        promise = promise.then(thenFn, rejectFn);
9365
      }
9366
 
9367
      promise.success = function(fn) {
9368
        promise.then(function(response) {
9369
          fn(response.data, response.status, response.headers, config);
9370
        });
9371
        return promise;
9372
      };
9373
 
9374
      promise.error = function(fn) {
9375
        promise.then(null, function(response) {
9376
          fn(response.data, response.status, response.headers, config);
9377
        });
9378
        return promise;
9379
      };
9380
 
9381
      return promise;
9382
 
9383
      function transformResponse(response) {
9384
        // make a copy since the response must be cacheable
9385
        var resp = extend({}, response);
9386
        if (!response.data) {
9387
          resp.data = response.data;
9388
        } else {
9389
          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
9390
        }
9391
        return (isSuccess(response.status))
9392
          ? resp
9393
          : $q.reject(resp);
9394
      }
9395
 
9396
      function executeHeaderFns(headers) {
9397
        var headerContent, processedHeaders = {};
9398
 
9399
        forEach(headers, function(headerFn, header) {
9400
          if (isFunction(headerFn)) {
9401
            headerContent = headerFn();
9402
            if (headerContent != null) {
9403
              processedHeaders[header] = headerContent;
9404
            }
9405
          } else {
9406
            processedHeaders[header] = headerFn;
9407
          }
9408
        });
9409
 
9410
        return processedHeaders;
9411
      }
9412
 
9413
      function mergeHeaders(config) {
9414
        var defHeaders = defaults.headers,
9415
            reqHeaders = extend({}, config.headers),
9416
            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
9417
 
9418
        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
9419
 
9420
        // using for-in instead of forEach to avoid unecessary iteration after header has been found
9421
        defaultHeadersIteration:
9422
        for (defHeaderName in defHeaders) {
9423
          lowercaseDefHeaderName = lowercase(defHeaderName);
9424
 
9425
          for (reqHeaderName in reqHeaders) {
9426
            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
9427
              continue defaultHeadersIteration;
9428
            }
9429
          }
9430
 
9431
          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
9432
        }
9433
 
9434
        // execute if header value is a function for merged headers
9435
        return executeHeaderFns(reqHeaders);
9436
      }
9437
    }
9438
 
9439
    $http.pendingRequests = [];
9440
 
9441
    /**
9442
     * @ngdoc method
9443
     * @name $http#get
9444
     *
9445
     * @description
9446
     * Shortcut method to perform `GET` request.
9447
     *
9448
     * @param {string} url Relative or absolute URL specifying the destination of the request
9449
     * @param {Object=} config Optional configuration object
9450
     * @returns {HttpPromise} Future object
9451
     */
9452
 
9453
    /**
9454
     * @ngdoc method
9455
     * @name $http#delete
9456
     *
9457
     * @description
9458
     * Shortcut method to perform `DELETE` request.
9459
     *
9460
     * @param {string} url Relative or absolute URL specifying the destination of the request
9461
     * @param {Object=} config Optional configuration object
9462
     * @returns {HttpPromise} Future object
9463
     */
9464
 
9465
    /**
9466
     * @ngdoc method
9467
     * @name $http#head
9468
     *
9469
     * @description
9470
     * Shortcut method to perform `HEAD` request.
9471
     *
9472
     * @param {string} url Relative or absolute URL specifying the destination of the request
9473
     * @param {Object=} config Optional configuration object
9474
     * @returns {HttpPromise} Future object
9475
     */
9476
 
9477
    /**
9478
     * @ngdoc method
9479
     * @name $http#jsonp
9480
     *
9481
     * @description
9482
     * Shortcut method to perform `JSONP` request.
9483
     *
9484
     * @param {string} url Relative or absolute URL specifying the destination of the request.
9485
     *                     The name of the callback should be the string `JSON_CALLBACK`.
9486
     * @param {Object=} config Optional configuration object
9487
     * @returns {HttpPromise} Future object
9488
     */
9489
    createShortMethods('get', 'delete', 'head', 'jsonp');
9490
 
9491
    /**
9492
     * @ngdoc method
9493
     * @name $http#post
9494
     *
9495
     * @description
9496
     * Shortcut method to perform `POST` request.
9497
     *
9498
     * @param {string} url Relative or absolute URL specifying the destination of the request
9499
     * @param {*} data Request content
9500
     * @param {Object=} config Optional configuration object
9501
     * @returns {HttpPromise} Future object
9502
     */
9503
 
9504
    /**
9505
     * @ngdoc method
9506
     * @name $http#put
9507
     *
9508
     * @description
9509
     * Shortcut method to perform `PUT` request.
9510
     *
9511
     * @param {string} url Relative or absolute URL specifying the destination of the request
9512
     * @param {*} data Request content
9513
     * @param {Object=} config Optional configuration object
9514
     * @returns {HttpPromise} Future object
9515
     */
9516
 
9517
     /**
9518
      * @ngdoc method
9519
      * @name $http#patch
9520
      *
9521
      * @description
9522
      * Shortcut method to perform `PATCH` request.
9523
      *
9524
      * @param {string} url Relative or absolute URL specifying the destination of the request
9525
      * @param {*} data Request content
9526
      * @param {Object=} config Optional configuration object
9527
      * @returns {HttpPromise} Future object
9528
      */
9529
    createShortMethodsWithData('post', 'put', 'patch');
9530
 
9531
        /**
9532
         * @ngdoc property
9533
         * @name $http#defaults
9534
         *
9535
         * @description
9536
         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
9537
         * default headers, withCredentials as well as request and response transformations.
9538
         *
9539
         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
9540
         */
9541
    $http.defaults = defaults;
9542
 
9543
 
9544
    return $http;
9545
 
9546
 
9547
    function createShortMethods(names) {
9548
      forEach(arguments, function(name) {
9549
        $http[name] = function(url, config) {
9550
          return $http(extend(config || {}, {
9551
            method: name,
9552
            url: url
9553
          }));
9554
        };
9555
      });
9556
    }
9557
 
9558
 
9559
    function createShortMethodsWithData(name) {
9560
      forEach(arguments, function(name) {
9561
        $http[name] = function(url, data, config) {
9562
          return $http(extend(config || {}, {
9563
            method: name,
9564
            url: url,
9565
            data: data
9566
          }));
9567
        };
9568
      });
9569
    }
9570
 
9571
 
9572
    /**
9573
     * Makes the request.
9574
     *
9575
     * !!! ACCESSES CLOSURE VARS:
9576
     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9577
     */
9578
    function sendReq(config, reqData) {
9579
      var deferred = $q.defer(),
9580
          promise = deferred.promise,
9581
          cache,
9582
          cachedResp,
9583
          reqHeaders = config.headers,
9584
          url = buildUrl(config.url, config.params);
9585
 
9586
      $http.pendingRequests.push(config);
9587
      promise.then(removePendingReq, removePendingReq);
9588
 
9589
 
9590
      if ((config.cache || defaults.cache) && config.cache !== false &&
9591
          (config.method === 'GET' || config.method === 'JSONP')) {
9592
        cache = isObject(config.cache) ? config.cache
9593
              : isObject(defaults.cache) ? defaults.cache
9594
              : defaultCache;
9595
      }
9596
 
9597
      if (cache) {
9598
        cachedResp = cache.get(url);
9599
        if (isDefined(cachedResp)) {
9600
          if (isPromiseLike(cachedResp)) {
9601
            // cached request has already been sent, but there is no response yet
9602
            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
9603
          } else {
9604
            // serving from cache
9605
            if (isArray(cachedResp)) {
9606
              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
9607
            } else {
9608
              resolvePromise(cachedResp, 200, {}, 'OK');
9609
            }
9610
          }
9611
        } else {
9612
          // put the promise for the non-transformed response into cache as a placeholder
9613
          cache.put(url, promise);
9614
        }
9615
      }
9616
 
9617
 
9618
      // if we won't have the response in cache, set the xsrf headers and
9619
      // send the request to the backend
9620
      if (isUndefined(cachedResp)) {
9621
        var xsrfValue = urlIsSameOrigin(config.url)
9622
            ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
9623
            : undefined;
9624
        if (xsrfValue) {
9625
          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
9626
        }
9627
 
9628
        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9629
            config.withCredentials, config.responseType);
9630
      }
9631
 
9632
      return promise;
9633
 
9634
 
9635
      /**
9636
       * Callback registered to $httpBackend():
9637
       *  - caches the response if desired
9638
       *  - resolves the raw $http promise
9639
       *  - calls $apply
9640
       */
9641
      function done(status, response, headersString, statusText) {
9642
        if (cache) {
9643
          if (isSuccess(status)) {
9644
            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
9645
          } else {
9646
            // remove promise from the cache
9647
            cache.remove(url);
9648
          }
9649
        }
9650
 
9651
        function resolveHttpPromise() {
9652
          resolvePromise(response, status, headersString, statusText);
9653
        }
9654
 
9655
        if (useApplyAsync) {
9656
          $rootScope.$applyAsync(resolveHttpPromise);
9657
        } else {
9658
          resolveHttpPromise();
9659
          if (!$rootScope.$$phase) $rootScope.$apply();
9660
        }
9661
      }
9662
 
9663
 
9664
      /**
9665
       * Resolves the raw $http promise.
9666
       */
9667
      function resolvePromise(response, status, headers, statusText) {
9668
        // normalize internal statuses to 0
9669
        status = Math.max(status, 0);
9670
 
9671
        (isSuccess(status) ? deferred.resolve : deferred.reject)({
9672
          data: response,
9673
          status: status,
9674
          headers: headersGetter(headers),
9675
          config: config,
9676
          statusText: statusText
9677
        });
9678
      }
9679
 
9680
      function resolvePromiseWithResult(result) {
9681
        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
9682
      }
9683
 
9684
      function removePendingReq() {
9685
        var idx = $http.pendingRequests.indexOf(config);
9686
        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
9687
      }
9688
    }
9689
 
9690
 
9691
    function buildUrl(url, params) {
9692
      if (!params) return url;
9693
      var parts = [];
9694
      forEachSorted(params, function(value, key) {
9695
        if (value === null || isUndefined(value)) return;
9696
        if (!isArray(value)) value = [value];
9697
 
9698
        forEach(value, function(v) {
9699
          if (isObject(v)) {
9700
            if (isDate(v)) {
9701
              v = v.toISOString();
9702
            } else {
9703
              v = toJson(v);
9704
            }
9705
          }
9706
          parts.push(encodeUriQuery(key) + '=' +
9707
                     encodeUriQuery(v));
9708
        });
9709
      });
9710
      if (parts.length > 0) {
9711
        url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9712
      }
9713
      return url;
9714
    }
9715
  }];
9716
}
9717
 
9718
function createXhr() {
9719
    return new window.XMLHttpRequest();
9720
}
9721
 
9722
/**
9723
 * @ngdoc service
9724
 * @name $httpBackend
9725
 * @requires $window
9726
 * @requires $document
9727
 *
9728
 * @description
9729
 * HTTP backend used by the {@link ng.$http service} that delegates to
9730
 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
9731
 *
9732
 * You should never need to use this service directly, instead use the higher-level abstractions:
9733
 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
9734
 *
9735
 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
9736
 * $httpBackend} which can be trained with responses.
9737
 */
9738
function $HttpBackendProvider() {
9739
  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
9740
    return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
9741
  }];
9742
}
9743
 
9744
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
9745
  // TODO(vojta): fix the signature
9746
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9747
    $browser.$$incOutstandingRequestCount();
9748
    url = url || $browser.url();
9749
 
9750
    if (lowercase(method) == 'jsonp') {
9751
      var callbackId = '_' + (callbacks.counter++).toString(36);
9752
      callbacks[callbackId] = function(data) {
9753
        callbacks[callbackId].data = data;
9754
        callbacks[callbackId].called = true;
9755
      };
9756
 
9757
      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
9758
          callbackId, function(status, text) {
9759
        completeRequest(callback, status, callbacks[callbackId].data, "", text);
9760
        callbacks[callbackId] = noop;
9761
      });
9762
    } else {
9763
 
9764
      var xhr = createXhr();
9765
 
9766
      xhr.open(method, url, true);
9767
      forEach(headers, function(value, key) {
9768
        if (isDefined(value)) {
9769
            xhr.setRequestHeader(key, value);
9770
        }
9771
      });
9772
 
9773
      xhr.onload = function requestLoaded() {
9774
        var statusText = xhr.statusText || '';
9775
 
9776
        // responseText is the old-school way of retrieving response (supported by IE8 & 9)
9777
        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
9778
        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
9779
 
9780
        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
9781
        var status = xhr.status === 1223 ? 204 : xhr.status;
9782
 
9783
        // fix status code when it is 0 (0 status is undocumented).
9784
        // Occurs when accessing file resources or on Android 4.1 stock browser
9785
        // while retrieving files from application cache.
9786
        if (status === 0) {
9787
          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
9788
        }
9789
 
9790
        completeRequest(callback,
9791
            status,
9792
            response,
9793
            xhr.getAllResponseHeaders(),
9794
            statusText);
9795
      };
9796
 
9797
      var requestError = function() {
9798
        // The response is always empty
9799
        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
9800
        completeRequest(callback, -1, null, null, '');
9801
      };
9802
 
9803
      xhr.onerror = requestError;
9804
      xhr.onabort = requestError;
9805
 
9806
      if (withCredentials) {
9807
        xhr.withCredentials = true;
9808
      }
9809
 
9810
      if (responseType) {
9811
        try {
9812
          xhr.responseType = responseType;
9813
        } catch (e) {
9814
          // WebKit added support for the json responseType value on 09/03/2013
9815
          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
9816
          // known to throw when setting the value "json" as the response type. Other older
9817
          // browsers implementing the responseType
9818
          //
9819
          // The json response type can be ignored if not supported, because JSON payloads are
9820
          // parsed on the client-side regardless.
9821
          if (responseType !== 'json') {
9822
            throw e;
9823
          }
9824
        }
9825
      }
9826
 
9827
      xhr.send(post || null);
9828
    }
9829
 
9830
    if (timeout > 0) {
9831
      var timeoutId = $browserDefer(timeoutRequest, timeout);
9832
    } else if (isPromiseLike(timeout)) {
9833
      timeout.then(timeoutRequest);
9834
    }
9835
 
9836
 
9837
    function timeoutRequest() {
9838
      jsonpDone && jsonpDone();
9839
      xhr && xhr.abort();
9840
    }
9841
 
9842
    function completeRequest(callback, status, response, headersString, statusText) {
9843
      // cancel timeout and subsequent timeout promise resolution
9844
      if (timeoutId !== undefined) {
9845
        $browserDefer.cancel(timeoutId);
9846
      }
9847
      jsonpDone = xhr = null;
9848
 
9849
      callback(status, response, headersString, statusText);
9850
      $browser.$$completeOutstandingRequest(noop);
9851
    }
9852
  };
9853
 
9854
  function jsonpReq(url, callbackId, done) {
9855
    // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
9856
    // - fetches local scripts via XHR and evals them
9857
    // - adds and immediately removes script elements from the document
9858
    var script = rawDocument.createElement('script'), callback = null;
9859
    script.type = "text/javascript";
9860
    script.src = url;
9861
    script.async = true;
9862
 
9863
    callback = function(event) {
9864
      removeEventListenerFn(script, "load", callback);
9865
      removeEventListenerFn(script, "error", callback);
9866
      rawDocument.body.removeChild(script);
9867
      script = null;
9868
      var status = -1;
9869
      var text = "unknown";
9870
 
9871
      if (event) {
9872
        if (event.type === "load" && !callbacks[callbackId].called) {
9873
          event = { type: "error" };
9874
        }
9875
        text = event.type;
9876
        status = event.type === "error" ? 404 : 200;
9877
      }
9878
 
9879
      if (done) {
9880
        done(status, text);
9881
      }
9882
    };
9883
 
9884
    addEventListenerFn(script, "load", callback);
9885
    addEventListenerFn(script, "error", callback);
9886
    rawDocument.body.appendChild(script);
9887
    return callback;
9888
  }
9889
}
9890
 
9891
var $interpolateMinErr = minErr('$interpolate');
9892
 
9893
/**
9894
 * @ngdoc provider
9895
 * @name $interpolateProvider
9896
 *
9897
 * @description
9898
 *
9899
 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
9900
 *
9901
 * @example
9902
<example module="customInterpolationApp">
9903
<file name="index.html">
9904
<script>
9905
  var customInterpolationApp = angular.module('customInterpolationApp', []);
9906
 
9907
  customInterpolationApp.config(function($interpolateProvider) {
9908
    $interpolateProvider.startSymbol('//');
9909
    $interpolateProvider.endSymbol('//');
9910
  });
9911
 
9912
 
9913
  customInterpolationApp.controller('DemoController', function() {
9914
      this.label = "This binding is brought you by // interpolation symbols.";
9915
  });
9916
</script>
9917
<div ng-app="App" ng-controller="DemoController as demo">
9918
    //demo.label//
9919
</div>
9920
</file>
9921
<file name="protractor.js" type="protractor">
9922
  it('should interpolate binding with custom symbols', function() {
9923
    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
9924
  });
9925
</file>
9926
</example>
9927
 */
9928
function $InterpolateProvider() {
9929
  var startSymbol = '{{';
9930
  var endSymbol = '}}';
9931
 
9932
  /**
9933
   * @ngdoc method
9934
   * @name $interpolateProvider#startSymbol
9935
   * @description
9936
   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
9937
   *
9938
   * @param {string=} value new value to set the starting symbol to.
9939
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9940
   */
9941
  this.startSymbol = function(value) {
9942
    if (value) {
9943
      startSymbol = value;
9944
      return this;
9945
    } else {
9946
      return startSymbol;
9947
    }
9948
  };
9949
 
9950
  /**
9951
   * @ngdoc method
9952
   * @name $interpolateProvider#endSymbol
9953
   * @description
9954
   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
9955
   *
9956
   * @param {string=} value new value to set the ending symbol to.
9957
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9958
   */
9959
  this.endSymbol = function(value) {
9960
    if (value) {
9961
      endSymbol = value;
9962
      return this;
9963
    } else {
9964
      return endSymbol;
9965
    }
9966
  };
9967
 
9968
 
9969
  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
9970
    var startSymbolLength = startSymbol.length,
9971
        endSymbolLength = endSymbol.length,
9972
        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
9973
        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
9974
 
9975
    function escape(ch) {
9976
      return '\\\\\\' + ch;
9977
    }
9978
 
9979
    /**
9980
     * @ngdoc service
9981
     * @name $interpolate
9982
     * @kind function
9983
     *
9984
     * @requires $parse
9985
     * @requires $sce
9986
     *
9987
     * @description
9988
     *
9989
     * Compiles a string with markup into an interpolation function. This service is used by the
9990
     * HTML {@link ng.$compile $compile} service for data binding. See
9991
     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
9992
     * interpolation markup.
9993
     *
9994
     *
9995
     * ```js
9996
     *   var $interpolate = ...; // injected
9997
     *   var exp = $interpolate('Hello {{name | uppercase}}!');
9998
     *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
9999
     * ```
10000
     *
10001
     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10002
     * `true`, the interpolation function will return `undefined` unless all embedded expressions
10003
     * evaluate to a value other than `undefined`.
10004
     *
10005
     * ```js
10006
     *   var $interpolate = ...; // injected
10007
     *   var context = {greeting: 'Hello', name: undefined };
10008
     *
10009
     *   // default "forgiving" mode
10010
     *   var exp = $interpolate('{{greeting}} {{name}}!');
10011
     *   expect(exp(context)).toEqual('Hello !');
10012
     *
10013
     *   // "allOrNothing" mode
10014
     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10015
     *   expect(exp(context)).toBeUndefined();
10016
     *   context.name = 'Angular';
10017
     *   expect(exp(context)).toEqual('Hello Angular!');
10018
     * ```
10019
     *
10020
     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10021
     *
10022
     * ####Escaped Interpolation
10023
     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10024
     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10025
     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10026
     * or binding.
10027
     *
10028
     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10029
     * degree, while also enabling code examples to work without relying on the
10030
     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
10031
     *
10032
     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
10033
     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
10034
     * interpolation start/end markers with their escaped counterparts.**
10035
     *
10036
     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
10037
     * output when the $interpolate service processes the text. So, for HTML elements interpolated
10038
     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
10039
     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
10040
     * this is typically useful only when user-data is used in rendering a template from the server, or
10041
     * when otherwise untrusted data is used by a directive.
10042
     *
10043
     * <example>
10044
     *  <file name="index.html">
10045
     *    <div ng-init="username='A user'">
10046
     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10047
     *        </p>
10048
     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
10049
     *        application, but fails to accomplish their task, because the server has correctly
10050
     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10051
     *        characters.</p>
10052
     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
10053
     *        from the database by an administrator.</p>
10054
     *    </div>
10055
     *  </file>
10056
     * </example>
10057
     *
10058
     * @param {string} text The text with markup to interpolate.
10059
     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10060
     *    embedded expression in order to return an interpolation function. Strings with no
10061
     *    embedded expression will return null for the interpolation function.
10062
     * @param {string=} trustedContext when provided, the returned function passes the interpolated
10063
     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10064
     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
10065
     *    provides Strict Contextual Escaping for details.
10066
     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10067
     *    unless all embedded expressions evaluate to a value other than `undefined`.
10068
     * @returns {function(context)} an interpolation function which is used to compute the
10069
     *    interpolated string. The function has these parameters:
10070
     *
10071
     * - `context`: evaluation context for all expressions embedded in the interpolated text
10072
     */
10073
    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
10074
      allOrNothing = !!allOrNothing;
10075
      var startIndex,
10076
          endIndex,
10077
          index = 0,
10078
          expressions = [],
10079
          parseFns = [],
10080
          textLength = text.length,
10081
          exp,
10082
          concat = [],
10083
          expressionPositions = [];
10084
 
10085
      while (index < textLength) {
10086
        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
10087
             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
10088
          if (index !== startIndex) {
10089
            concat.push(unescapeText(text.substring(index, startIndex)));
10090
          }
10091
          exp = text.substring(startIndex + startSymbolLength, endIndex);
10092
          expressions.push(exp);
10093
          parseFns.push($parse(exp, parseStringifyInterceptor));
10094
          index = endIndex + endSymbolLength;
10095
          expressionPositions.push(concat.length);
10096
          concat.push('');
10097
        } else {
10098
          // we did not find an interpolation, so we have to add the remainder to the separators array
10099
          if (index !== textLength) {
10100
            concat.push(unescapeText(text.substring(index)));
10101
          }
10102
          break;
10103
        }
10104
      }
10105
 
10106
      // Concatenating expressions makes it hard to reason about whether some combination of
10107
      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
10108
      // single expression be used for iframe[src], object[src], etc., we ensure that the value
10109
      // that's used is assigned or constructed by some JS code somewhere that is more testable or
10110
      // make it obvious that you bound the value to some user controlled value.  This helps reduce
10111
      // the load when auditing for XSS issues.
10112
      if (trustedContext && concat.length > 1) {
10113
          throw $interpolateMinErr('noconcat',
10114
              "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10115
              "interpolations that concatenate multiple expressions when a trusted value is " +
10116
              "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10117
      }
10118
 
10119
      if (!mustHaveExpression || expressions.length) {
10120
        var compute = function(values) {
10121
          for (var i = 0, ii = expressions.length; i < ii; i++) {
10122
            if (allOrNothing && isUndefined(values[i])) return;
10123
            concat[expressionPositions[i]] = values[i];
10124
          }
10125
          return concat.join('');
10126
        };
10127
 
10128
        var getValue = function(value) {
10129
          return trustedContext ?
10130
            $sce.getTrusted(trustedContext, value) :
10131
            $sce.valueOf(value);
10132
        };
10133
 
10134
        var stringify = function(value) {
10135
          if (value == null) { // null || undefined
10136
            return '';
10137
          }
10138
          switch (typeof value) {
10139
            case 'string':
10140
              break;
10141
            case 'number':
10142
              value = '' + value;
10143
              break;
10144
            default:
10145
              value = toJson(value);
10146
          }
10147
 
10148
          return value;
10149
        };
10150
 
10151
        return extend(function interpolationFn(context) {
10152
            var i = 0;
10153
            var ii = expressions.length;
10154
            var values = new Array(ii);
10155
 
10156
            try {
10157
              for (; i < ii; i++) {
10158
                values[i] = parseFns[i](context);
10159
              }
10160
 
10161
              return compute(values);
10162
            } catch (err) {
10163
              var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10164
                  err.toString());
10165
              $exceptionHandler(newErr);
10166
            }
10167
 
10168
          }, {
10169
          // all of these properties are undocumented for now
10170
          exp: text, //just for compatibility with regular watchers created via $watch
10171
          expressions: expressions,
10172
          $$watchDelegate: function(scope, listener, objectEquality) {
10173
            var lastValue;
10174
            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10175
              var currValue = compute(values);
10176
              if (isFunction(listener)) {
10177
                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10178
              }
10179
              lastValue = currValue;
10180
            }, objectEquality);
10181
          }
10182
        });
10183
      }
10184
 
10185
      function unescapeText(text) {
10186
        return text.replace(escapedStartRegexp, startSymbol).
10187
          replace(escapedEndRegexp, endSymbol);
10188
      }
10189
 
10190
      function parseStringifyInterceptor(value) {
10191
        try {
10192
          value = getValue(value);
10193
          return allOrNothing && !isDefined(value) ? value : stringify(value);
10194
        } catch (err) {
10195
          var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10196
            err.toString());
10197
          $exceptionHandler(newErr);
10198
        }
10199
      }
10200
    }
10201
 
10202
 
10203
    /**
10204
     * @ngdoc method
10205
     * @name $interpolate#startSymbol
10206
     * @description
10207
     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10208
     *
10209
     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10210
     * the symbol.
10211
     *
10212
     * @returns {string} start symbol.
10213
     */
10214
    $interpolate.startSymbol = function() {
10215
      return startSymbol;
10216
    };
10217
 
10218
 
10219
    /**
10220
     * @ngdoc method
10221
     * @name $interpolate#endSymbol
10222
     * @description
10223
     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10224
     *
10225
     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10226
     * the symbol.
10227
     *
10228
     * @returns {string} end symbol.
10229
     */
10230
    $interpolate.endSymbol = function() {
10231
      return endSymbol;
10232
    };
10233
 
10234
    return $interpolate;
10235
  }];
10236
}
10237
 
10238
function $IntervalProvider() {
10239
  this.$get = ['$rootScope', '$window', '$q', '$$q',
10240
       function($rootScope,   $window,   $q,   $$q) {
10241
    var intervals = {};
10242
 
10243
 
10244
     /**
10245
      * @ngdoc service
10246
      * @name $interval
10247
      *
10248
      * @description
10249
      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10250
      * milliseconds.
10251
      *
10252
      * The return value of registering an interval function is a promise. This promise will be
10253
      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10254
      * run indefinitely if `count` is not defined. The value of the notification will be the
10255
      * number of iterations that have run.
10256
      * To cancel an interval, call `$interval.cancel(promise)`.
10257
      *
10258
      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10259
      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10260
      * time.
10261
      *
10262
      * <div class="alert alert-warning">
10263
      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10264
      * with them.  In particular they are not automatically destroyed when a controller's scope or a
10265
      * directive's element are destroyed.
10266
      * You should take this into consideration and make sure to always cancel the interval at the
10267
      * appropriate moment.  See the example below for more details on how and when to do this.
10268
      * </div>
10269
      *
10270
      * @param {function()} fn A function that should be called repeatedly.
10271
      * @param {number} delay Number of milliseconds between each function call.
10272
      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10273
      *   indefinitely.
10274
      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10275
      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10276
      * @returns {promise} A promise which will be notified on each iteration.
10277
      *
10278
      * @example
10279
      * <example module="intervalExample">
10280
      * <file name="index.html">
10281
      *   <script>
10282
      *     angular.module('intervalExample', [])
10283
      *       .controller('ExampleController', ['$scope', '$interval',
10284
      *         function($scope, $interval) {
10285
      *           $scope.format = 'M/d/yy h:mm:ss a';
10286
      *           $scope.blood_1 = 100;
10287
      *           $scope.blood_2 = 120;
10288
      *
10289
      *           var stop;
10290
      *           $scope.fight = function() {
10291
      *             // Don't start a new fight if we are already fighting
10292
      *             if ( angular.isDefined(stop) ) return;
10293
      *
10294
      *             stop = $interval(function() {
10295
      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10296
      *                 $scope.blood_1 = $scope.blood_1 - 3;
10297
      *                 $scope.blood_2 = $scope.blood_2 - 4;
10298
      *               } else {
10299
      *                 $scope.stopFight();
10300
      *               }
10301
      *             }, 100);
10302
      *           };
10303
      *
10304
      *           $scope.stopFight = function() {
10305
      *             if (angular.isDefined(stop)) {
10306
      *               $interval.cancel(stop);
10307
      *               stop = undefined;
10308
      *             }
10309
      *           };
10310
      *
10311
      *           $scope.resetFight = function() {
10312
      *             $scope.blood_1 = 100;
10313
      *             $scope.blood_2 = 120;
10314
      *           };
10315
      *
10316
      *           $scope.$on('$destroy', function() {
10317
      *             // Make sure that the interval is destroyed too
10318
      *             $scope.stopFight();
10319
      *           });
10320
      *         }])
10321
      *       // Register the 'myCurrentTime' directive factory method.
10322
      *       // We inject $interval and dateFilter service since the factory method is DI.
10323
      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
10324
      *         function($interval, dateFilter) {
10325
      *           // return the directive link function. (compile function not needed)
10326
      *           return function(scope, element, attrs) {
10327
      *             var format,  // date format
10328
      *                 stopTime; // so that we can cancel the time updates
10329
      *
10330
      *             // used to update the UI
10331
      *             function updateTime() {
10332
      *               element.text(dateFilter(new Date(), format));
10333
      *             }
10334
      *
10335
      *             // watch the expression, and update the UI on change.
10336
      *             scope.$watch(attrs.myCurrentTime, function(value) {
10337
      *               format = value;
10338
      *               updateTime();
10339
      *             });
10340
      *
10341
      *             stopTime = $interval(updateTime, 1000);
10342
      *
10343
      *             // listen on DOM destroy (removal) event, and cancel the next UI update
10344
      *             // to prevent updating time after the DOM element was removed.
10345
      *             element.on('$destroy', function() {
10346
      *               $interval.cancel(stopTime);
10347
      *             });
10348
      *           }
10349
      *         }]);
10350
      *   </script>
10351
      *
10352
      *   <div>
10353
      *     <div ng-controller="ExampleController">
10354
      *       Date format: <input ng-model="format"> <hr/>
10355
      *       Current time is: <span my-current-time="format"></span>
10356
      *       <hr/>
10357
      *       Blood 1 : <font color='red'>{{blood_1}}</font>
10358
      *       Blood 2 : <font color='red'>{{blood_2}}</font>
10359
      *       <button type="button" data-ng-click="fight()">Fight</button>
10360
      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
10361
      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
10362
      *     </div>
10363
      *   </div>
10364
      *
10365
      * </file>
10366
      * </example>
10367
      */
10368
    function interval(fn, delay, count, invokeApply) {
10369
      var setInterval = $window.setInterval,
10370
          clearInterval = $window.clearInterval,
10371
          iteration = 0,
10372
          skipApply = (isDefined(invokeApply) && !invokeApply),
10373
          deferred = (skipApply ? $$q : $q).defer(),
10374
          promise = deferred.promise;
10375
 
10376
      count = isDefined(count) ? count : 0;
10377
 
10378
      promise.then(null, null, fn);
10379
 
10380
      promise.$$intervalId = setInterval(function tick() {
10381
        deferred.notify(iteration++);
10382
 
10383
        if (count > 0 && iteration >= count) {
10384
          deferred.resolve(iteration);
10385
          clearInterval(promise.$$intervalId);
10386
          delete intervals[promise.$$intervalId];
10387
        }
10388
 
10389
        if (!skipApply) $rootScope.$apply();
10390
 
10391
      }, delay);
10392
 
10393
      intervals[promise.$$intervalId] = deferred;
10394
 
10395
      return promise;
10396
    }
10397
 
10398
 
10399
     /**
10400
      * @ngdoc method
10401
      * @name $interval#cancel
10402
      *
10403
      * @description
10404
      * Cancels a task associated with the `promise`.
10405
      *
10406
      * @param {promise} promise returned by the `$interval` function.
10407
      * @returns {boolean} Returns `true` if the task was successfully canceled.
10408
      */
10409
    interval.cancel = function(promise) {
10410
      if (promise && promise.$$intervalId in intervals) {
10411
        intervals[promise.$$intervalId].reject('canceled');
10412
        $window.clearInterval(promise.$$intervalId);
10413
        delete intervals[promise.$$intervalId];
10414
        return true;
10415
      }
10416
      return false;
10417
    };
10418
 
10419
    return interval;
10420
  }];
10421
}
10422
 
10423
/**
10424
 * @ngdoc service
10425
 * @name $locale
10426
 *
10427
 * @description
10428
 * $locale service provides localization rules for various Angular components. As of right now the
10429
 * only public api is:
10430
 *
10431
 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
10432
 */
10433
function $LocaleProvider() {
10434
  this.$get = function() {
10435
    return {
10436
      id: 'en-us',
10437
 
10438
      NUMBER_FORMATS: {
10439
        DECIMAL_SEP: '.',
10440
        GROUP_SEP: ',',
10441
        PATTERNS: [
10442
          { // Decimal Pattern
10443
            minInt: 1,
10444
            minFrac: 0,
10445
            maxFrac: 3,
10446
            posPre: '',
10447
            posSuf: '',
10448
            negPre: '-',
10449
            negSuf: '',
10450
            gSize: 3,
10451
            lgSize: 3
10452
          },{ //Currency Pattern
10453
            minInt: 1,
10454
            minFrac: 2,
10455
            maxFrac: 2,
10456
            posPre: '\u00A4',
10457
            posSuf: '',
10458
            negPre: '(\u00A4',
10459
            negSuf: ')',
10460
            gSize: 3,
10461
            lgSize: 3
10462
          }
10463
        ],
10464
        CURRENCY_SYM: '$'
10465
      },
10466
 
10467
      DATETIME_FORMATS: {
10468
        MONTH:
10469
            'January,February,March,April,May,June,July,August,September,October,November,December'
10470
            .split(','),
10471
        SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
10472
        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
10473
        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
10474
        AMPMS: ['AM','PM'],
10475
        medium: 'MMM d, y h:mm:ss a',
10476
        'short': 'M/d/yy h:mm a',
10477
        fullDate: 'EEEE, MMMM d, y',
10478
        longDate: 'MMMM d, y',
10479
        mediumDate: 'MMM d, y',
10480
        shortDate: 'M/d/yy',
10481
        mediumTime: 'h:mm:ss a',
10482
        shortTime: 'h:mm a'
10483
      },
10484
 
10485
      pluralCat: function(num) {
10486
        if (num === 1) {
10487
          return 'one';
10488
        }
10489
        return 'other';
10490
      }
10491
    };
10492
  };
10493
}
10494
 
10495
var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
10496
    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
10497
var $locationMinErr = minErr('$location');
10498
 
10499
 
10500
/**
10501
 * Encode path using encodeUriSegment, ignoring forward slashes
10502
 *
10503
 * @param {string} path Path to encode
10504
 * @returns {string}
10505
 */
10506
function encodePath(path) {
10507
  var segments = path.split('/'),
10508
      i = segments.length;
10509
 
10510
  while (i--) {
10511
    segments[i] = encodeUriSegment(segments[i]);
10512
  }
10513
 
10514
  return segments.join('/');
10515
}
10516
 
10517
function parseAbsoluteUrl(absoluteUrl, locationObj) {
10518
  var parsedUrl = urlResolve(absoluteUrl);
10519
 
10520
  locationObj.$$protocol = parsedUrl.protocol;
10521
  locationObj.$$host = parsedUrl.hostname;
10522
  locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
10523
}
10524
 
10525
 
10526
function parseAppUrl(relativeUrl, locationObj) {
10527
  var prefixed = (relativeUrl.charAt(0) !== '/');
10528
  if (prefixed) {
10529
    relativeUrl = '/' + relativeUrl;
10530
  }
10531
  var match = urlResolve(relativeUrl);
10532
  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10533
      match.pathname.substring(1) : match.pathname);
10534
  locationObj.$$search = parseKeyValue(match.search);
10535
  locationObj.$$hash = decodeURIComponent(match.hash);
10536
 
10537
  // make sure path starts with '/';
10538
  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
10539
    locationObj.$$path = '/' + locationObj.$$path;
10540
  }
10541
}
10542
 
10543
 
10544
/**
10545
 *
10546
 * @param {string} begin
10547
 * @param {string} whole
10548
 * @returns {string} returns text from whole after begin or undefined if it does not begin with
10549
 *                   expected string.
10550
 */
10551
function beginsWith(begin, whole) {
10552
  if (whole.indexOf(begin) === 0) {
10553
    return whole.substr(begin.length);
10554
  }
10555
}
10556
 
10557
 
10558
function stripHash(url) {
10559
  var index = url.indexOf('#');
10560
  return index == -1 ? url : url.substr(0, index);
10561
}
10562
 
10563
function trimEmptyHash(url) {
10564
  return url.replace(/(#.+)|#$/, '$1');
10565
}
10566
 
10567
 
10568
function stripFile(url) {
10569
  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
10570
}
10571
 
10572
/* return the server only (scheme://host:port) */
10573
function serverBase(url) {
10574
  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
10575
}
10576
 
10577
 
10578
/**
10579
 * LocationHtml5Url represents an url
10580
 * This object is exposed as $location service when HTML5 mode is enabled and supported
10581
 *
10582
 * @constructor
10583
 * @param {string} appBase application base URL
10584
 * @param {string} basePrefix url path prefix
10585
 */
10586
function LocationHtml5Url(appBase, basePrefix) {
10587
  this.$$html5 = true;
10588
  basePrefix = basePrefix || '';
10589
  var appBaseNoFile = stripFile(appBase);
10590
  parseAbsoluteUrl(appBase, this);
10591
 
10592
 
10593
  /**
10594
   * Parse given html5 (regular) url string into properties
10595
   * @param {string} url HTML5 url
10596
   * @private
10597
   */
10598
  this.$$parse = function(url) {
10599
    var pathUrl = beginsWith(appBaseNoFile, url);
10600
    if (!isString(pathUrl)) {
10601
      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
10602
          appBaseNoFile);
10603
    }
10604
 
10605
    parseAppUrl(pathUrl, this);
10606
 
10607
    if (!this.$$path) {
10608
      this.$$path = '/';
10609
    }
10610
 
10611
    this.$$compose();
10612
  };
10613
 
10614
  /**
10615
   * Compose url and update `absUrl` property
10616
   * @private
10617
   */
10618
  this.$$compose = function() {
10619
    var search = toKeyValue(this.$$search),
10620
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10621
 
10622
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10623
    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
10624
  };
10625
 
10626
  this.$$parseLinkUrl = function(url, relHref) {
10627
    if (relHref && relHref[0] === '#') {
10628
      // special case for links to hash fragments:
10629
      // keep the old url and only replace the hash fragment
10630
      this.hash(relHref.slice(1));
10631
      return true;
10632
    }
10633
    var appUrl, prevAppUrl;
10634
    var rewrittenUrl;
10635
 
10636
    if ((appUrl = beginsWith(appBase, url)) !== undefined) {
10637
      prevAppUrl = appUrl;
10638
      if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
10639
        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
10640
      } else {
10641
        rewrittenUrl = appBase + prevAppUrl;
10642
      }
10643
    } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
10644
      rewrittenUrl = appBaseNoFile + appUrl;
10645
    } else if (appBaseNoFile == url + '/') {
10646
      rewrittenUrl = appBaseNoFile;
10647
    }
10648
    if (rewrittenUrl) {
10649
      this.$$parse(rewrittenUrl);
10650
    }
10651
    return !!rewrittenUrl;
10652
  };
10653
}
10654
 
10655
 
10656
/**
10657
 * LocationHashbangUrl represents url
10658
 * This object is exposed as $location service when developer doesn't opt into html5 mode.
10659
 * It also serves as the base class for html5 mode fallback on legacy browsers.
10660
 *
10661
 * @constructor
10662
 * @param {string} appBase application base URL
10663
 * @param {string} hashPrefix hashbang prefix
10664
 */
10665
function LocationHashbangUrl(appBase, hashPrefix) {
10666
  var appBaseNoFile = stripFile(appBase);
10667
 
10668
  parseAbsoluteUrl(appBase, this);
10669
 
10670
 
10671
  /**
10672
   * Parse given hashbang url into properties
10673
   * @param {string} url Hashbang url
10674
   * @private
10675
   */
10676
  this.$$parse = function(url) {
10677
    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
10678
    var withoutHashUrl;
10679
 
10680
    if (withoutBaseUrl.charAt(0) === '#') {
10681
 
10682
      // The rest of the url starts with a hash so we have
10683
      // got either a hashbang path or a plain hash fragment
10684
      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
10685
      if (isUndefined(withoutHashUrl)) {
10686
        // There was no hashbang prefix so we just have a hash fragment
10687
        withoutHashUrl = withoutBaseUrl;
10688
      }
10689
 
10690
    } else {
10691
      // There was no hashbang path nor hash fragment:
10692
      // If we are in HTML5 mode we use what is left as the path;
10693
      // Otherwise we ignore what is left
10694
      withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
10695
    }
10696
 
10697
    parseAppUrl(withoutHashUrl, this);
10698
 
10699
    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10700
 
10701
    this.$$compose();
10702
 
10703
    /*
10704
     * In Windows, on an anchor node on documents loaded from
10705
     * the filesystem, the browser will return a pathname
10706
     * prefixed with the drive name ('/C:/path') when a
10707
     * pathname without a drive is set:
10708
     *  * a.setAttribute('href', '/foo')
10709
     *   * a.pathname === '/C:/foo' //true
10710
     *
10711
     * Inside of Angular, we're always using pathnames that
10712
     * do not include drive names for routing.
10713
     */
10714
    function removeWindowsDriveName(path, url, base) {
10715
      /*
10716
      Matches paths for file protocol on windows,
10717
      such as /C:/foo/bar, and captures only /foo/bar.
10718
      */
10719
      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
10720
 
10721
      var firstPathSegmentMatch;
10722
 
10723
      //Get the relative path from the input URL.
10724
      if (url.indexOf(base) === 0) {
10725
        url = url.replace(base, '');
10726
      }
10727
 
10728
      // The input URL intentionally contains a first path segment that ends with a colon.
10729
      if (windowsFilePathExp.exec(url)) {
10730
        return path;
10731
      }
10732
 
10733
      firstPathSegmentMatch = windowsFilePathExp.exec(path);
10734
      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
10735
    }
10736
  };
10737
 
10738
  /**
10739
   * Compose hashbang url and update `absUrl` property
10740
   * @private
10741
   */
10742
  this.$$compose = function() {
10743
    var search = toKeyValue(this.$$search),
10744
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10745
 
10746
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10747
    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
10748
  };
10749
 
10750
  this.$$parseLinkUrl = function(url, relHref) {
10751
    if (stripHash(appBase) == stripHash(url)) {
10752
      this.$$parse(url);
10753
      return true;
10754
    }
10755
    return false;
10756
  };
10757
}
10758
 
10759
 
10760
/**
10761
 * LocationHashbangUrl represents url
10762
 * This object is exposed as $location service when html5 history api is enabled but the browser
10763
 * does not support it.
10764
 *
10765
 * @constructor
10766
 * @param {string} appBase application base URL
10767
 * @param {string} hashPrefix hashbang prefix
10768
 */
10769
function LocationHashbangInHtml5Url(appBase, hashPrefix) {
10770
  this.$$html5 = true;
10771
  LocationHashbangUrl.apply(this, arguments);
10772
 
10773
  var appBaseNoFile = stripFile(appBase);
10774
 
10775
  this.$$parseLinkUrl = function(url, relHref) {
10776
    if (relHref && relHref[0] === '#') {
10777
      // special case for links to hash fragments:
10778
      // keep the old url and only replace the hash fragment
10779
      this.hash(relHref.slice(1));
10780
      return true;
10781
    }
10782
 
10783
    var rewrittenUrl;
10784
    var appUrl;
10785
 
10786
    if (appBase == stripHash(url)) {
10787
      rewrittenUrl = url;
10788
    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
10789
      rewrittenUrl = appBase + hashPrefix + appUrl;
10790
    } else if (appBaseNoFile === url + '/') {
10791
      rewrittenUrl = appBaseNoFile;
10792
    }
10793
    if (rewrittenUrl) {
10794
      this.$$parse(rewrittenUrl);
10795
    }
10796
    return !!rewrittenUrl;
10797
  };
10798
 
10799
  this.$$compose = function() {
10800
    var search = toKeyValue(this.$$search),
10801
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10802
 
10803
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10804
    // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
10805
    this.$$absUrl = appBase + hashPrefix + this.$$url;
10806
  };
10807
 
10808
}
10809
 
10810
 
10811
var locationPrototype = {
10812
 
10813
  /**
10814
   * Are we in html5 mode?
10815
   * @private
10816
   */
10817
  $$html5: false,
10818
 
10819
  /**
10820
   * Has any change been replacing?
10821
   * @private
10822
   */
10823
  $$replace: false,
10824
 
10825
  /**
10826
   * @ngdoc method
10827
   * @name $location#absUrl
10828
   *
10829
   * @description
10830
   * This method is getter only.
10831
   *
10832
   * Return full url representation with all segments encoded according to rules specified in
10833
   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10834
   *
10835
   *
10836
   * ```js
10837
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10838
   * var absUrl = $location.absUrl();
10839
   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10840
   * ```
10841
   *
10842
   * @return {string} full url
10843
   */
10844
  absUrl: locationGetter('$$absUrl'),
10845
 
10846
  /**
10847
   * @ngdoc method
10848
   * @name $location#url
10849
   *
10850
   * @description
10851
   * This method is getter / setter.
10852
   *
10853
   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
10854
   *
10855
   * Change path, search and hash, when called with parameter and return `$location`.
10856
   *
10857
   *
10858
   * ```js
10859
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10860
   * var url = $location.url();
10861
   * // => "/some/path?foo=bar&baz=xoxo"
10862
   * ```
10863
   *
10864
   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10865
   * @return {string} url
10866
   */
10867
  url: function(url) {
10868
    if (isUndefined(url))
10869
      return this.$$url;
10870
 
10871
    var match = PATH_MATCH.exec(url);
10872
    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10873
    if (match[2] || match[1] || url === '') this.search(match[3] || '');
10874
    this.hash(match[5] || '');
10875
 
10876
    return this;
10877
  },
10878
 
10879
  /**
10880
   * @ngdoc method
10881
   * @name $location#protocol
10882
   *
10883
   * @description
10884
   * This method is getter only.
10885
   *
10886
   * Return protocol of current url.
10887
   *
10888
   *
10889
   * ```js
10890
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10891
   * var protocol = $location.protocol();
10892
   * // => "http"
10893
   * ```
10894
   *
10895
   * @return {string} protocol of current url
10896
   */
10897
  protocol: locationGetter('$$protocol'),
10898
 
10899
  /**
10900
   * @ngdoc method
10901
   * @name $location#host
10902
   *
10903
   * @description
10904
   * This method is getter only.
10905
   *
10906
   * Return host of current url.
10907
   *
10908
   *
10909
   * ```js
10910
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10911
   * var host = $location.host();
10912
   * // => "example.com"
10913
   * ```
10914
   *
10915
   * @return {string} host of current url.
10916
   */
10917
  host: locationGetter('$$host'),
10918
 
10919
  /**
10920
   * @ngdoc method
10921
   * @name $location#port
10922
   *
10923
   * @description
10924
   * This method is getter only.
10925
   *
10926
   * Return port of current url.
10927
   *
10928
   *
10929
   * ```js
10930
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10931
   * var port = $location.port();
10932
   * // => 80
10933
   * ```
10934
   *
10935
   * @return {Number} port
10936
   */
10937
  port: locationGetter('$$port'),
10938
 
10939
  /**
10940
   * @ngdoc method
10941
   * @name $location#path
10942
   *
10943
   * @description
10944
   * This method is getter / setter.
10945
   *
10946
   * Return path of current url when called without any parameter.
10947
   *
10948
   * Change path when called with parameter and return `$location`.
10949
   *
10950
   * Note: Path should always begin with forward slash (/), this method will add the forward slash
10951
   * if it is missing.
10952
   *
10953
   *
10954
   * ```js
10955
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10956
   * var path = $location.path();
10957
   * // => "/some/path"
10958
   * ```
10959
   *
10960
   * @param {(string|number)=} path New path
10961
   * @return {string} path
10962
   */
10963
  path: locationGetterSetter('$$path', function(path) {
10964
    path = path !== null ? path.toString() : '';
10965
    return path.charAt(0) == '/' ? path : '/' + path;
10966
  }),
10967
 
10968
  /**
10969
   * @ngdoc method
10970
   * @name $location#search
10971
   *
10972
   * @description
10973
   * This method is getter / setter.
10974
   *
10975
   * Return search part (as object) of current url when called without any parameter.
10976
   *
10977
   * Change search part when called with parameter and return `$location`.
10978
   *
10979
   *
10980
   * ```js
10981
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10982
   * var searchObject = $location.search();
10983
   * // => {foo: 'bar', baz: 'xoxo'}
10984
   *
10985
   * // set foo to 'yipee'
10986
   * $location.search('foo', 'yipee');
10987
   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
10988
   * ```
10989
   *
10990
   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
10991
   * hash object.
10992
   *
10993
   * When called with a single argument the method acts as a setter, setting the `search` component
10994
   * of `$location` to the specified value.
10995
   *
10996
   * If the argument is a hash object containing an array of values, these values will be encoded
10997
   * as duplicate search parameters in the url.
10998
   *
10999
   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11000
   * will override only a single search property.
11001
   *
11002
   * If `paramValue` is an array, it will override the property of the `search` component of
11003
   * `$location` specified via the first argument.
11004
   *
11005
   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11006
   *
11007
   * If `paramValue` is `true`, the property specified via the first argument will be added with no
11008
   * value nor trailing equal sign.
11009
   *
11010
   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11011
   * one or more arguments returns `$location` object itself.
11012
   */
11013
  search: function(search, paramValue) {
11014
    switch (arguments.length) {
11015
      case 0:
11016
        return this.$$search;
11017
      case 1:
11018
        if (isString(search) || isNumber(search)) {
11019
          search = search.toString();
11020
          this.$$search = parseKeyValue(search);
11021
        } else if (isObject(search)) {
11022
          search = copy(search, {});
11023
          // remove object undefined or null properties
11024
          forEach(search, function(value, key) {
11025
            if (value == null) delete search[key];
11026
          });
11027
 
11028
          this.$$search = search;
11029
        } else {
11030
          throw $locationMinErr('isrcharg',
11031
              'The first argument of the `$location#search()` call must be a string or an object.');
11032
        }
11033
        break;
11034
      default:
11035
        if (isUndefined(paramValue) || paramValue === null) {
11036
          delete this.$$search[search];
11037
        } else {
11038
          this.$$search[search] = paramValue;
11039
        }
11040
    }
11041
 
11042
    this.$$compose();
11043
    return this;
11044
  },
11045
 
11046
  /**
11047
   * @ngdoc method
11048
   * @name $location#hash
11049
   *
11050
   * @description
11051
   * This method is getter / setter.
11052
   *
11053
   * Return hash fragment when called without any parameter.
11054
   *
11055
   * Change hash fragment when called with parameter and return `$location`.
11056
   *
11057
   *
11058
   * ```js
11059
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11060
   * var hash = $location.hash();
11061
   * // => "hashValue"
11062
   * ```
11063
   *
11064
   * @param {(string|number)=} hash New hash fragment
11065
   * @return {string} hash
11066
   */
11067
  hash: locationGetterSetter('$$hash', function(hash) {
11068
    return hash !== null ? hash.toString() : '';
11069
  }),
11070
 
11071
  /**
11072
   * @ngdoc method
11073
   * @name $location#replace
11074
   *
11075
   * @description
11076
   * If called, all changes to $location during current `$digest` will be replacing current history
11077
   * record, instead of adding new one.
11078
   */
11079
  replace: function() {
11080
    this.$$replace = true;
11081
    return this;
11082
  }
11083
};
11084
 
11085
forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11086
  Location.prototype = Object.create(locationPrototype);
11087
 
11088
  /**
11089
   * @ngdoc method
11090
   * @name $location#state
11091
   *
11092
   * @description
11093
   * This method is getter / setter.
11094
   *
11095
   * Return the history state object when called without any parameter.
11096
   *
11097
   * Change the history state object when called with one parameter and return `$location`.
11098
   * The state object is later passed to `pushState` or `replaceState`.
11099
   *
11100
   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11101
   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11102
   * older browsers (like IE9 or Android < 4.0), don't use this method.
11103
   *
11104
   * @param {object=} state State object for pushState or replaceState
11105
   * @return {object} state
11106
   */
11107
  Location.prototype.state = function(state) {
11108
    if (!arguments.length)
11109
      return this.$$state;
11110
 
11111
    if (Location !== LocationHtml5Url || !this.$$html5) {
11112
      throw $locationMinErr('nostate', 'History API state support is available only ' +
11113
        'in HTML5 mode and only in browsers supporting HTML5 History API');
11114
    }
11115
    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11116
    // but we're changing the $$state reference to $browser.state() during the $digest
11117
    // so the modification window is narrow.
11118
    this.$$state = isUndefined(state) ? null : state;
11119
 
11120
    return this;
11121
  };
11122
});
11123
 
11124
 
11125
function locationGetter(property) {
11126
  return function() {
11127
    return this[property];
11128
  };
11129
}
11130
 
11131
 
11132
function locationGetterSetter(property, preprocess) {
11133
  return function(value) {
11134
    if (isUndefined(value))
11135
      return this[property];
11136
 
11137
    this[property] = preprocess(value);
11138
    this.$$compose();
11139
 
11140
    return this;
11141
  };
11142
}
11143
 
11144
 
11145
/**
11146
 * @ngdoc service
11147
 * @name $location
11148
 *
11149
 * @requires $rootElement
11150
 *
11151
 * @description
11152
 * The $location service parses the URL in the browser address bar (based on the
11153
 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11154
 * available to your application. Changes to the URL in the address bar are reflected into
11155
 * $location service and changes to $location are reflected into the browser address bar.
11156
 *
11157
 * **The $location service:**
11158
 *
11159
 * - Exposes the current URL in the browser address bar, so you can
11160
 *   - Watch and observe the URL.
11161
 *   - Change the URL.
11162
 * - Synchronizes the URL with the browser when the user
11163
 *   - Changes the address bar.
11164
 *   - Clicks the back or forward button (or clicks a History link).
11165
 *   - Clicks on a link.
11166
 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11167
 *
11168
 * For more information see {@link guide/$location Developer Guide: Using $location}
11169
 */
11170
 
11171
/**
11172
 * @ngdoc provider
11173
 * @name $locationProvider
11174
 * @description
11175
 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11176
 */
11177
function $LocationProvider() {
11178
  var hashPrefix = '',
11179
      html5Mode = {
11180
        enabled: false,
11181
        requireBase: true,
11182
        rewriteLinks: true
11183
      };
11184
 
11185
  /**
11186
   * @ngdoc method
11187
   * @name $locationProvider#hashPrefix
11188
   * @description
11189
   * @param {string=} prefix Prefix for hash part (containing path and search)
11190
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
11191
   */
11192
  this.hashPrefix = function(prefix) {
11193
    if (isDefined(prefix)) {
11194
      hashPrefix = prefix;
11195
      return this;
11196
    } else {
11197
      return hashPrefix;
11198
    }
11199
  };
11200
 
11201
  /**
11202
   * @ngdoc method
11203
   * @name $locationProvider#html5Mode
11204
   * @description
11205
   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11206
   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11207
   *   properties:
11208
   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11209
   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11210
   *     support `pushState`.
11211
   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11212
   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11213
   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
11214
   *     See the {@link guide/$location $location guide for more information}
11215
   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11216
   *     enables/disables url rewriting for relative links.
11217
   *
11218
   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11219
   */
11220
  this.html5Mode = function(mode) {
11221
    if (isBoolean(mode)) {
11222
      html5Mode.enabled = mode;
11223
      return this;
11224
    } else if (isObject(mode)) {
11225
 
11226
      if (isBoolean(mode.enabled)) {
11227
        html5Mode.enabled = mode.enabled;
11228
      }
11229
 
11230
      if (isBoolean(mode.requireBase)) {
11231
        html5Mode.requireBase = mode.requireBase;
11232
      }
11233
 
11234
      if (isBoolean(mode.rewriteLinks)) {
11235
        html5Mode.rewriteLinks = mode.rewriteLinks;
11236
      }
11237
 
11238
      return this;
11239
    } else {
11240
      return html5Mode;
11241
    }
11242
  };
11243
 
11244
  /**
11245
   * @ngdoc event
11246
   * @name $location#$locationChangeStart
11247
   * @eventType broadcast on root scope
11248
   * @description
11249
   * Broadcasted before a URL will change.
11250
   *
11251
   * This change can be prevented by calling
11252
   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11253
   * details about event object. Upon successful change
11254
   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11255
   *
11256
   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11257
   * the browser supports the HTML5 History API.
11258
   *
11259
   * @param {Object} angularEvent Synthetic event object.
11260
   * @param {string} newUrl New URL
11261
   * @param {string=} oldUrl URL that was before it was changed.
11262
   * @param {string=} newState New history state object
11263
   * @param {string=} oldState History state object that was before it was changed.
11264
   */
11265
 
11266
  /**
11267
   * @ngdoc event
11268
   * @name $location#$locationChangeSuccess
11269
   * @eventType broadcast on root scope
11270
   * @description
11271
   * Broadcasted after a URL was changed.
11272
   *
11273
   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11274
   * the browser supports the HTML5 History API.
11275
   *
11276
   * @param {Object} angularEvent Synthetic event object.
11277
   * @param {string} newUrl New URL
11278
   * @param {string=} oldUrl URL that was before it was changed.
11279
   * @param {string=} newState New history state object
11280
   * @param {string=} oldState History state object that was before it was changed.
11281
   */
11282
 
11283
  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
11284
      function($rootScope, $browser, $sniffer, $rootElement, $window) {
11285
    var $location,
11286
        LocationMode,
11287
        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11288
        initialUrl = $browser.url(),
11289
        appBase;
11290
 
11291
    if (html5Mode.enabled) {
11292
      if (!baseHref && html5Mode.requireBase) {
11293
        throw $locationMinErr('nobase',
11294
          "$location in HTML5 mode requires a <base> tag to be present!");
11295
      }
11296
      appBase = serverBase(initialUrl) + (baseHref || '/');
11297
      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
11298
    } else {
11299
      appBase = stripHash(initialUrl);
11300
      LocationMode = LocationHashbangUrl;
11301
    }
11302
    $location = new LocationMode(appBase, '#' + hashPrefix);
11303
    $location.$$parseLinkUrl(initialUrl, initialUrl);
11304
 
11305
    $location.$$state = $browser.state();
11306
 
11307
    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
11308
 
11309
    function setBrowserUrlWithFallback(url, replace, state) {
11310
      var oldUrl = $location.url();
11311
      var oldState = $location.$$state;
11312
      try {
11313
        $browser.url(url, replace, state);
11314
 
11315
        // Make sure $location.state() returns referentially identical (not just deeply equal)
11316
        // state object; this makes possible quick checking if the state changed in the digest
11317
        // loop. Checking deep equality would be too expensive.
11318
        $location.$$state = $browser.state();
11319
      } catch (e) {
11320
        // Restore old values if pushState fails
11321
        $location.url(oldUrl);
11322
        $location.$$state = oldState;
11323
 
11324
        throw e;
11325
      }
11326
    }
11327
 
11328
    $rootElement.on('click', function(event) {
11329
      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
11330
      // currently we open nice url link and redirect then
11331
 
11332
      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
11333
 
11334
      var elm = jqLite(event.target);
11335
 
11336
      // traverse the DOM up to find first A tag
11337
      while (nodeName_(elm[0]) !== 'a') {
11338
        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
11339
        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
11340
      }
11341
 
11342
      var absHref = elm.prop('href');
11343
      // get the actual href attribute - see
11344
      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
11345
      var relHref = elm.attr('href') || elm.attr('xlink:href');
11346
 
11347
      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
11348
        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
11349
        // an animation.
11350
        absHref = urlResolve(absHref.animVal).href;
11351
      }
11352
 
11353
      // Ignore when url is started with javascript: or mailto:
11354
      if (IGNORE_URI_REGEXP.test(absHref)) return;
11355
 
11356
      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
11357
        if ($location.$$parseLinkUrl(absHref, relHref)) {
11358
          // We do a preventDefault for all urls that are part of the angular application,
11359
          // in html5mode and also without, so that we are able to abort navigation without
11360
          // getting double entries in the location history.
11361
          event.preventDefault();
11362
          // update location manually
11363
          if ($location.absUrl() != $browser.url()) {
11364
            $rootScope.$apply();
11365
            // hack to work around FF6 bug 684208 when scenario runner clicks on links
11366
            $window.angular['ff-684208-preventDefault'] = true;
11367
          }
11368
        }
11369
      }
11370
    });
11371
 
11372
 
11373
    // rewrite hashbang url <> html5 url
11374
    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
11375
      $browser.url($location.absUrl(), true);
11376
    }
11377
 
11378
    var initializing = true;
11379
 
11380
    // update $location when $browser url changes
11381
    $browser.onUrlChange(function(newUrl, newState) {
11382
      $rootScope.$evalAsync(function() {
11383
        var oldUrl = $location.absUrl();
11384
        var oldState = $location.$$state;
11385
        var defaultPrevented;
11386
 
11387
        $location.$$parse(newUrl);
11388
        $location.$$state = newState;
11389
 
11390
        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11391
            newState, oldState).defaultPrevented;
11392
 
11393
        // if the location was changed by a `$locationChangeStart` handler then stop
11394
        // processing this location change
11395
        if ($location.absUrl() !== newUrl) return;
11396
 
11397
        if (defaultPrevented) {
11398
          $location.$$parse(oldUrl);
11399
          $location.$$state = oldState;
11400
          setBrowserUrlWithFallback(oldUrl, false, oldState);
11401
        } else {
11402
          initializing = false;
11403
          afterLocationChange(oldUrl, oldState);
11404
        }
11405
      });
11406
      if (!$rootScope.$$phase) $rootScope.$digest();
11407
    });
11408
 
11409
    // update browser
11410
    $rootScope.$watch(function $locationWatch() {
11411
      var oldUrl = trimEmptyHash($browser.url());
11412
      var newUrl = trimEmptyHash($location.absUrl());
11413
      var oldState = $browser.state();
11414
      var currentReplace = $location.$$replace;
11415
      var urlOrStateChanged = oldUrl !== newUrl ||
11416
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
11417
 
11418
      if (initializing || urlOrStateChanged) {
11419
        initializing = false;
11420
 
11421
        $rootScope.$evalAsync(function() {
11422
          var newUrl = $location.absUrl();
11423
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11424
              $location.$$state, oldState).defaultPrevented;
11425
 
11426
          // if the location was changed by a `$locationChangeStart` handler then stop
11427
          // processing this location change
11428
          if ($location.absUrl() !== newUrl) return;
11429
 
11430
          if (defaultPrevented) {
11431
            $location.$$parse(oldUrl);
11432
            $location.$$state = oldState;
11433
          } else {
11434
            if (urlOrStateChanged) {
11435
              setBrowserUrlWithFallback(newUrl, currentReplace,
11436
                                        oldState === $location.$$state ? null : $location.$$state);
11437
            }
11438
            afterLocationChange(oldUrl, oldState);
11439
          }
11440
        });
11441
      }
11442
 
11443
      $location.$$replace = false;
11444
 
11445
      // we don't need to return anything because $evalAsync will make the digest loop dirty when
11446
      // there is a change
11447
    });
11448
 
11449
    return $location;
11450
 
11451
    function afterLocationChange(oldUrl, oldState) {
11452
      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
11453
        $location.$$state, oldState);
11454
    }
11455
}];
11456
}
11457
 
11458
/**
11459
 * @ngdoc service
11460
 * @name $log
11461
 * @requires $window
11462
 *
11463
 * @description
11464
 * Simple service for logging. Default implementation safely writes the message
11465
 * into the browser's console (if present).
11466
 *
11467
 * The main purpose of this service is to simplify debugging and troubleshooting.
11468
 *
11469
 * The default is to log `debug` messages. You can use
11470
 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
11471
 *
11472
 * @example
11473
   <example module="logExample">
11474
     <file name="script.js">
11475
       angular.module('logExample', [])
11476
         .controller('LogController', ['$scope', '$log', function($scope, $log) {
11477
           $scope.$log = $log;
11478
           $scope.message = 'Hello World!';
11479
         }]);
11480
     </file>
11481
     <file name="index.html">
11482
       <div ng-controller="LogController">
11483
         <p>Reload this page with open console, enter text and hit the log button...</p>
11484
         Message:
11485
         <input type="text" ng-model="message"/>
11486
         <button ng-click="$log.log(message)">log</button>
11487
         <button ng-click="$log.warn(message)">warn</button>
11488
         <button ng-click="$log.info(message)">info</button>
11489
         <button ng-click="$log.error(message)">error</button>
11490
       </div>
11491
     </file>
11492
   </example>
11493
 */
11494
 
11495
/**
11496
 * @ngdoc provider
11497
 * @name $logProvider
11498
 * @description
11499
 * Use the `$logProvider` to configure how the application logs messages
11500
 */
11501
function $LogProvider() {
11502
  var debug = true,
11503
      self = this;
11504
 
11505
  /**
11506
   * @ngdoc method
11507
   * @name $logProvider#debugEnabled
11508
   * @description
11509
   * @param {boolean=} flag enable or disable debug level messages
11510
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
11511
   */
11512
  this.debugEnabled = function(flag) {
11513
    if (isDefined(flag)) {
11514
      debug = flag;
11515
    return this;
11516
    } else {
11517
      return debug;
11518
    }
11519
  };
11520
 
11521
  this.$get = ['$window', function($window) {
11522
    return {
11523
      /**
11524
       * @ngdoc method
11525
       * @name $log#log
11526
       *
11527
       * @description
11528
       * Write a log message
11529
       */
11530
      log: consoleLog('log'),
11531
 
11532
      /**
11533
       * @ngdoc method
11534
       * @name $log#info
11535
       *
11536
       * @description
11537
       * Write an information message
11538
       */
11539
      info: consoleLog('info'),
11540
 
11541
      /**
11542
       * @ngdoc method
11543
       * @name $log#warn
11544
       *
11545
       * @description
11546
       * Write a warning message
11547
       */
11548
      warn: consoleLog('warn'),
11549
 
11550
      /**
11551
       * @ngdoc method
11552
       * @name $log#error
11553
       *
11554
       * @description
11555
       * Write an error message
11556
       */
11557
      error: consoleLog('error'),
11558
 
11559
      /**
11560
       * @ngdoc method
11561
       * @name $log#debug
11562
       *
11563
       * @description
11564
       * Write a debug message
11565
       */
11566
      debug: (function() {
11567
        var fn = consoleLog('debug');
11568
 
11569
        return function() {
11570
          if (debug) {
11571
            fn.apply(self, arguments);
11572
          }
11573
        };
11574
      }())
11575
    };
11576
 
11577
    function formatError(arg) {
11578
      if (arg instanceof Error) {
11579
        if (arg.stack) {
11580
          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
11581
              ? 'Error: ' + arg.message + '\n' + arg.stack
11582
              : arg.stack;
11583
        } else if (arg.sourceURL) {
11584
          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
11585
        }
11586
      }
11587
      return arg;
11588
    }
11589
 
11590
    function consoleLog(type) {
11591
      var console = $window.console || {},
11592
          logFn = console[type] || console.log || noop,
11593
          hasApply = false;
11594
 
11595
      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
11596
      // The reason behind this is that console.log has type "object" in IE8...
11597
      try {
11598
        hasApply = !!logFn.apply;
11599
      } catch (e) {}
11600
 
11601
      if (hasApply) {
11602
        return function() {
11603
          var args = [];
11604
          forEach(arguments, function(arg) {
11605
            args.push(formatError(arg));
11606
          });
11607
          return logFn.apply(console, args);
11608
        };
11609
      }
11610
 
11611
      // we are IE which either doesn't have window.console => this is noop and we do nothing,
11612
      // or we are IE where console.log doesn't have apply so we log at least first 2 args
11613
      return function(arg1, arg2) {
11614
        logFn(arg1, arg2 == null ? '' : arg2);
11615
      };
11616
    }
11617
  }];
11618
}
11619
 
11620
var $parseMinErr = minErr('$parse');
11621
 
11622
// Sandboxing Angular Expressions
11623
// ------------------------------
11624
// Angular expressions are generally considered safe because these expressions only have direct
11625
// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11626
// obtaining a reference to native JS functions such as the Function constructor.
11627
//
11628
// As an example, consider the following Angular expression:
11629
//
11630
//   {}.toString.constructor('alert("evil JS code")')
11631
//
11632
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11633
// against the expression language, but not to prevent exploits that were enabled by exposing
11634
// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11635
// practice and therefore we are not even trying to protect against interaction with an object
11636
// explicitly exposed in this way.
11637
//
11638
// In general, it is not possible to access a Window object from an angular expression unless a
11639
// window or some DOM object that has a reference to window is published onto a Scope.
11640
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11641
// native objects.
11642
//
11643
// See https://docs.angularjs.org/guide/security
11644
 
11645
 
11646
function ensureSafeMemberName(name, fullExpression) {
11647
  if (name === "__defineGetter__" || name === "__defineSetter__"
11648
      || name === "__lookupGetter__" || name === "__lookupSetter__"
11649
      || name === "__proto__") {
11650
    throw $parseMinErr('isecfld',
11651
        'Attempting to access a disallowed field in Angular expressions! '
11652
        + 'Expression: {0}', fullExpression);
11653
  }
11654
  return name;
11655
}
11656
 
11657
function ensureSafeObject(obj, fullExpression) {
11658
  // nifty check if obj is Function that is fast and works across iframes and other contexts
11659
  if (obj) {
11660
    if (obj.constructor === obj) {
11661
      throw $parseMinErr('isecfn',
11662
          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11663
          fullExpression);
11664
    } else if (// isWindow(obj)
11665
        obj.window === obj) {
11666
      throw $parseMinErr('isecwindow',
11667
          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
11668
          fullExpression);
11669
    } else if (// isElement(obj)
11670
        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
11671
      throw $parseMinErr('isecdom',
11672
          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
11673
          fullExpression);
11674
    } else if (// block Object so that we can't get hold of dangerous Object.* methods
11675
        obj === Object) {
11676
      throw $parseMinErr('isecobj',
11677
          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
11678
          fullExpression);
11679
    }
11680
  }
11681
  return obj;
11682
}
11683
 
11684
var CALL = Function.prototype.call;
11685
var APPLY = Function.prototype.apply;
11686
var BIND = Function.prototype.bind;
11687
 
11688
function ensureSafeFunction(obj, fullExpression) {
11689
  if (obj) {
11690
    if (obj.constructor === obj) {
11691
      throw $parseMinErr('isecfn',
11692
        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11693
        fullExpression);
11694
    } else if (obj === CALL || obj === APPLY || obj === BIND) {
11695
      throw $parseMinErr('isecff',
11696
        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
11697
        fullExpression);
11698
    }
11699
  }
11700
}
11701
 
11702
//Keyword constants
11703
var CONSTANTS = createMap();
11704
forEach({
11705
  'null': function() { return null; },
11706
  'true': function() { return true; },
11707
  'false': function() { return false; },
11708
  'undefined': function() {}
11709
}, function(constantGetter, name) {
11710
  constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
11711
  CONSTANTS[name] = constantGetter;
11712
});
11713
 
11714
//Not quite a constant, but can be lex/parsed the same
11715
CONSTANTS['this'] = function(self) { return self; };
11716
CONSTANTS['this'].sharedGetter = true;
11717
 
11718
 
11719
//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
11720
var OPERATORS = extend(createMap(), {
11721
    '+':function(self, locals, a, b) {
11722
      a=a(self, locals); b=b(self, locals);
11723
      if (isDefined(a)) {
11724
        if (isDefined(b)) {
11725
          return a + b;
11726
        }
11727
        return a;
11728
      }
11729
      return isDefined(b) ? b : undefined;},
11730
    '-':function(self, locals, a, b) {
11731
          a=a(self, locals); b=b(self, locals);
11732
          return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11733
        },
11734
    '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11735
    '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11736
    '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11737
    '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11738
    '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11739
    '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11740
    '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11741
    '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11742
    '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11743
    '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11744
    '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11745
    '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11746
    '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11747
    '!':function(self, locals, a) {return !a(self, locals);},
11748
 
11749
    //Tokenized as operators but parsed as assignment/filters
11750
    '=':true,
11751
    '|':true
11752
});
11753
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
11754
 
11755
 
11756
/////////////////////////////////////////
11757
 
11758
 
11759
/**
11760
 * @constructor
11761
 */
11762
var Lexer = function(options) {
11763
  this.options = options;
11764
};
11765
 
11766
Lexer.prototype = {
11767
  constructor: Lexer,
11768
 
11769
  lex: function(text) {
11770
    this.text = text;
11771
    this.index = 0;
11772
    this.tokens = [];
11773
 
11774
    while (this.index < this.text.length) {
11775
      var ch = this.text.charAt(this.index);
11776
      if (ch === '"' || ch === "'") {
11777
        this.readString(ch);
11778
      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11779
        this.readNumber();
11780
      } else if (this.isIdent(ch)) {
11781
        this.readIdent();
11782
      } else if (this.is(ch, '(){}[].,;:?')) {
11783
        this.tokens.push({index: this.index, text: ch});
11784
        this.index++;
11785
      } else if (this.isWhitespace(ch)) {
11786
        this.index++;
11787
      } else {
11788
        var ch2 = ch + this.peek();
11789
        var ch3 = ch2 + this.peek(2);
11790
        var op1 = OPERATORS[ch];
11791
        var op2 = OPERATORS[ch2];
11792
        var op3 = OPERATORS[ch3];
11793
        if (op1 || op2 || op3) {
11794
          var token = op3 ? ch3 : (op2 ? ch2 : ch);
11795
          this.tokens.push({index: this.index, text: token, operator: true});
11796
          this.index += token.length;
11797
        } else {
11798
          this.throwError('Unexpected next character ', this.index, this.index + 1);
11799
        }
11800
      }
11801
    }
11802
    return this.tokens;
11803
  },
11804
 
11805
  is: function(ch, chars) {
11806
    return chars.indexOf(ch) !== -1;
11807
  },
11808
 
11809
  peek: function(i) {
11810
    var num = i || 1;
11811
    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
11812
  },
11813
 
11814
  isNumber: function(ch) {
11815
    return ('0' <= ch && ch <= '9') && typeof ch === "string";
11816
  },
11817
 
11818
  isWhitespace: function(ch) {
11819
    // IE treats non-breaking space as \u00A0
11820
    return (ch === ' ' || ch === '\r' || ch === '\t' ||
11821
            ch === '\n' || ch === '\v' || ch === '\u00A0');
11822
  },
11823
 
11824
  isIdent: function(ch) {
11825
    return ('a' <= ch && ch <= 'z' ||
11826
            'A' <= ch && ch <= 'Z' ||
11827
            '_' === ch || ch === '$');
11828
  },
11829
 
11830
  isExpOperator: function(ch) {
11831
    return (ch === '-' || ch === '+' || this.isNumber(ch));
11832
  },
11833
 
11834
  throwError: function(error, start, end) {
11835
    end = end || this.index;
11836
    var colStr = (isDefined(start)
11837
            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
11838
            : ' ' + end);
11839
    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
11840
        error, colStr, this.text);
11841
  },
11842
 
11843
  readNumber: function() {
11844
    var number = '';
11845
    var start = this.index;
11846
    while (this.index < this.text.length) {
11847
      var ch = lowercase(this.text.charAt(this.index));
11848
      if (ch == '.' || this.isNumber(ch)) {
11849
        number += ch;
11850
      } else {
11851
        var peekCh = this.peek();
11852
        if (ch == 'e' && this.isExpOperator(peekCh)) {
11853
          number += ch;
11854
        } else if (this.isExpOperator(ch) &&
11855
            peekCh && this.isNumber(peekCh) &&
11856
            number.charAt(number.length - 1) == 'e') {
11857
          number += ch;
11858
        } else if (this.isExpOperator(ch) &&
11859
            (!peekCh || !this.isNumber(peekCh)) &&
11860
            number.charAt(number.length - 1) == 'e') {
11861
          this.throwError('Invalid exponent');
11862
        } else {
11863
          break;
11864
        }
11865
      }
11866
      this.index++;
11867
    }
11868
    this.tokens.push({
11869
      index: start,
11870
      text: number,
11871
      constant: true,
11872
      value: Number(number)
11873
    });
11874
  },
11875
 
11876
  readIdent: function() {
11877
    var start = this.index;
11878
    while (this.index < this.text.length) {
11879
      var ch = this.text.charAt(this.index);
11880
      if (!(this.isIdent(ch) || this.isNumber(ch))) {
11881
        break;
11882
      }
11883
      this.index++;
11884
    }
11885
    this.tokens.push({
11886
      index: start,
11887
      text: this.text.slice(start, this.index),
11888
      identifier: true
11889
    });
11890
  },
11891
 
11892
  readString: function(quote) {
11893
    var start = this.index;
11894
    this.index++;
11895
    var string = '';
11896
    var rawString = quote;
11897
    var escape = false;
11898
    while (this.index < this.text.length) {
11899
      var ch = this.text.charAt(this.index);
11900
      rawString += ch;
11901
      if (escape) {
11902
        if (ch === 'u') {
11903
          var hex = this.text.substring(this.index + 1, this.index + 5);
11904
          if (!hex.match(/[\da-f]{4}/i))
11905
            this.throwError('Invalid unicode escape [\\u' + hex + ']');
11906
          this.index += 4;
11907
          string += String.fromCharCode(parseInt(hex, 16));
11908
        } else {
11909
          var rep = ESCAPE[ch];
11910
          string = string + (rep || ch);
11911
        }
11912
        escape = false;
11913
      } else if (ch === '\\') {
11914
        escape = true;
11915
      } else if (ch === quote) {
11916
        this.index++;
11917
        this.tokens.push({
11918
          index: start,
11919
          text: rawString,
11920
          constant: true,
11921
          value: string
11922
        });
11923
        return;
11924
      } else {
11925
        string += ch;
11926
      }
11927
      this.index++;
11928
    }
11929
    this.throwError('Unterminated quote', start);
11930
  }
11931
};
11932
 
11933
 
11934
function isConstant(exp) {
11935
  return exp.constant;
11936
}
11937
 
11938
/**
11939
 * @constructor
11940
 */
11941
var Parser = function(lexer, $filter, options) {
11942
  this.lexer = lexer;
11943
  this.$filter = $filter;
11944
  this.options = options;
11945
};
11946
 
11947
Parser.ZERO = extend(function() {
11948
  return 0;
11949
}, {
11950
  sharedGetter: true,
11951
  constant: true
11952
});
11953
 
11954
Parser.prototype = {
11955
  constructor: Parser,
11956
 
11957
  parse: function(text) {
11958
    this.text = text;
11959
    this.tokens = this.lexer.lex(text);
11960
 
11961
    var value = this.statements();
11962
 
11963
    if (this.tokens.length !== 0) {
11964
      this.throwError('is an unexpected token', this.tokens[0]);
11965
    }
11966
 
11967
    value.literal = !!value.literal;
11968
    value.constant = !!value.constant;
11969
 
11970
    return value;
11971
  },
11972
 
11973
  primary: function() {
11974
    var primary;
11975
    if (this.expect('(')) {
11976
      primary = this.filterChain();
11977
      this.consume(')');
11978
    } else if (this.expect('[')) {
11979
      primary = this.arrayDeclaration();
11980
    } else if (this.expect('{')) {
11981
      primary = this.object();
11982
    } else if (this.peek().identifier && this.peek().text in CONSTANTS) {
11983
      primary = CONSTANTS[this.consume().text];
11984
    } else if (this.peek().identifier) {
11985
      primary = this.identifier();
11986
    } else if (this.peek().constant) {
11987
      primary = this.constant();
11988
    } else {
11989
      this.throwError('not a primary expression', this.peek());
11990
    }
11991
 
11992
    var next, context;
11993
    while ((next = this.expect('(', '[', '.'))) {
11994
      if (next.text === '(') {
11995
        primary = this.functionCall(primary, context);
11996
        context = null;
11997
      } else if (next.text === '[') {
11998
        context = primary;
11999
        primary = this.objectIndex(primary);
12000
      } else if (next.text === '.') {
12001
        context = primary;
12002
        primary = this.fieldAccess(primary);
12003
      } else {
12004
        this.throwError('IMPOSSIBLE');
12005
      }
12006
    }
12007
    return primary;
12008
  },
12009
 
12010
  throwError: function(msg, token) {
12011
    throw $parseMinErr('syntax',
12012
        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
12013
          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
12014
  },
12015
 
12016
  peekToken: function() {
12017
    if (this.tokens.length === 0)
12018
      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12019
    return this.tokens[0];
12020
  },
12021
 
12022
  peek: function(e1, e2, e3, e4) {
12023
    return this.peekAhead(0, e1, e2, e3, e4);
12024
  },
12025
  peekAhead: function(i, e1, e2, e3, e4) {
12026
    if (this.tokens.length > i) {
12027
      var token = this.tokens[i];
12028
      var t = token.text;
12029
      if (t === e1 || t === e2 || t === e3 || t === e4 ||
12030
          (!e1 && !e2 && !e3 && !e4)) {
12031
        return token;
12032
      }
12033
    }
12034
    return false;
12035
  },
12036
 
12037
  expect: function(e1, e2, e3, e4) {
12038
    var token = this.peek(e1, e2, e3, e4);
12039
    if (token) {
12040
      this.tokens.shift();
12041
      return token;
12042
    }
12043
    return false;
12044
  },
12045
 
12046
  consume: function(e1) {
12047
    if (this.tokens.length === 0) {
12048
      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12049
    }
12050
 
12051
    var token = this.expect(e1);
12052
    if (!token) {
12053
      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
12054
    }
12055
    return token;
12056
  },
12057
 
12058
  unaryFn: function(op, right) {
12059
    var fn = OPERATORS[op];
12060
    return extend(function $parseUnaryFn(self, locals) {
12061
      return fn(self, locals, right);
12062
    }, {
12063
      constant:right.constant,
12064
      inputs: [right]
12065
    });
12066
  },
12067
 
12068
  binaryFn: function(left, op, right, isBranching) {
12069
    var fn = OPERATORS[op];
12070
    return extend(function $parseBinaryFn(self, locals) {
12071
      return fn(self, locals, left, right);
12072
    }, {
12073
      constant: left.constant && right.constant,
12074
      inputs: !isBranching && [left, right]
12075
    });
12076
  },
12077
 
12078
  identifier: function() {
12079
    var id = this.consume().text;
12080
 
12081
    //Continue reading each `.identifier` unless it is a method invocation
12082
    while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
12083
      id += this.consume().text + this.consume().text;
12084
    }
12085
 
12086
    return getterFn(id, this.options, this.text);
12087
  },
12088
 
12089
  constant: function() {
12090
    var value = this.consume().value;
12091
 
12092
    return extend(function $parseConstant() {
12093
      return value;
12094
    }, {
12095
      constant: true,
12096
      literal: true
12097
    });
12098
  },
12099
 
12100
  statements: function() {
12101
    var statements = [];
12102
    while (true) {
12103
      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12104
        statements.push(this.filterChain());
12105
      if (!this.expect(';')) {
12106
        // optimize for the common case where there is only one statement.
12107
        // TODO(size): maybe we should not support multiple statements?
12108
        return (statements.length === 1)
12109
            ? statements[0]
12110
            : function $parseStatements(self, locals) {
12111
                var value;
12112
                for (var i = 0, ii = statements.length; i < ii; i++) {
12113
                  value = statements[i](self, locals);
12114
                }
12115
                return value;
12116
              };
12117
      }
12118
    }
12119
  },
12120
 
12121
  filterChain: function() {
12122
    var left = this.expression();
12123
    var token;
12124
    while ((token = this.expect('|'))) {
12125
      left = this.filter(left);
12126
    }
12127
    return left;
12128
  },
12129
 
12130
  filter: function(inputFn) {
12131
    var fn = this.$filter(this.consume().text);
12132
    var argsFn;
12133
    var args;
12134
 
12135
    if (this.peek(':')) {
12136
      argsFn = [];
12137
      args = []; // we can safely reuse the array
12138
      while (this.expect(':')) {
12139
        argsFn.push(this.expression());
12140
      }
12141
    }
12142
 
12143
    var inputs = [inputFn].concat(argsFn || []);
12144
 
12145
    return extend(function $parseFilter(self, locals) {
12146
      var input = inputFn(self, locals);
12147
      if (args) {
12148
        args[0] = input;
12149
 
12150
        var i = argsFn.length;
12151
        while (i--) {
12152
          args[i + 1] = argsFn[i](self, locals);
12153
        }
12154
 
12155
        return fn.apply(undefined, args);
12156
      }
12157
 
12158
      return fn(input);
12159
    }, {
12160
      constant: !fn.$stateful && inputs.every(isConstant),
12161
      inputs: !fn.$stateful && inputs
12162
    });
12163
  },
12164
 
12165
  expression: function() {
12166
    return this.assignment();
12167
  },
12168
 
12169
  assignment: function() {
12170
    var left = this.ternary();
12171
    var right;
12172
    var token;
12173
    if ((token = this.expect('='))) {
12174
      if (!left.assign) {
12175
        this.throwError('implies assignment but [' +
12176
            this.text.substring(0, token.index) + '] can not be assigned to', token);
12177
      }
12178
      right = this.ternary();
12179
      return extend(function $parseAssignment(scope, locals) {
12180
        return left.assign(scope, right(scope, locals), locals);
12181
      }, {
12182
        inputs: [left, right]
12183
      });
12184
    }
12185
    return left;
12186
  },
12187
 
12188
  ternary: function() {
12189
    var left = this.logicalOR();
12190
    var middle;
12191
    var token;
12192
    if ((token = this.expect('?'))) {
12193
      middle = this.assignment();
12194
      if (this.consume(':')) {
12195
        var right = this.assignment();
12196
 
12197
        return extend(function $parseTernary(self, locals) {
12198
          return left(self, locals) ? middle(self, locals) : right(self, locals);
12199
        }, {
12200
          constant: left.constant && middle.constant && right.constant
12201
        });
12202
      }
12203
    }
12204
 
12205
    return left;
12206
  },
12207
 
12208
  logicalOR: function() {
12209
    var left = this.logicalAND();
12210
    var token;
12211
    while ((token = this.expect('||'))) {
12212
      left = this.binaryFn(left, token.text, this.logicalAND(), true);
12213
    }
12214
    return left;
12215
  },
12216
 
12217
  logicalAND: function() {
12218
    var left = this.equality();
12219
    var token;
12220
    while ((token = this.expect('&&'))) {
12221
      left = this.binaryFn(left, token.text, this.equality(), true);
12222
    }
12223
    return left;
12224
  },
12225
 
12226
  equality: function() {
12227
    var left = this.relational();
12228
    var token;
12229
    while ((token = this.expect('==','!=','===','!=='))) {
12230
      left = this.binaryFn(left, token.text, this.relational());
12231
    }
12232
    return left;
12233
  },
12234
 
12235
  relational: function() {
12236
    var left = this.additive();
12237
    var token;
12238
    while ((token = this.expect('<', '>', '<=', '>='))) {
12239
      left = this.binaryFn(left, token.text, this.additive());
12240
    }
12241
    return left;
12242
  },
12243
 
12244
  additive: function() {
12245
    var left = this.multiplicative();
12246
    var token;
12247
    while ((token = this.expect('+','-'))) {
12248
      left = this.binaryFn(left, token.text, this.multiplicative());
12249
    }
12250
    return left;
12251
  },
12252
 
12253
  multiplicative: function() {
12254
    var left = this.unary();
12255
    var token;
12256
    while ((token = this.expect('*','/','%'))) {
12257
      left = this.binaryFn(left, token.text, this.unary());
12258
    }
12259
    return left;
12260
  },
12261
 
12262
  unary: function() {
12263
    var token;
12264
    if (this.expect('+')) {
12265
      return this.primary();
12266
    } else if ((token = this.expect('-'))) {
12267
      return this.binaryFn(Parser.ZERO, token.text, this.unary());
12268
    } else if ((token = this.expect('!'))) {
12269
      return this.unaryFn(token.text, this.unary());
12270
    } else {
12271
      return this.primary();
12272
    }
12273
  },
12274
 
12275
  fieldAccess: function(object) {
12276
    var getter = this.identifier();
12277
 
12278
    return extend(function $parseFieldAccess(scope, locals, self) {
12279
      var o = self || object(scope, locals);
12280
      return (o == null) ? undefined : getter(o);
12281
    }, {
12282
      assign: function(scope, value, locals) {
12283
        var o = object(scope, locals);
12284
        if (!o) object.assign(scope, o = {}, locals);
12285
        return getter.assign(o, value);
12286
      }
12287
    });
12288
  },
12289
 
12290
  objectIndex: function(obj) {
12291
    var expression = this.text;
12292
 
12293
    var indexFn = this.expression();
12294
    this.consume(']');
12295
 
12296
    return extend(function $parseObjectIndex(self, locals) {
12297
      var o = obj(self, locals),
12298
          i = indexFn(self, locals),
12299
          v;
12300
 
12301
      ensureSafeMemberName(i, expression);
12302
      if (!o) return undefined;
12303
      v = ensureSafeObject(o[i], expression);
12304
      return v;
12305
    }, {
12306
      assign: function(self, value, locals) {
12307
        var key = ensureSafeMemberName(indexFn(self, locals), expression);
12308
        // prevent overwriting of Function.constructor which would break ensureSafeObject check
12309
        var o = ensureSafeObject(obj(self, locals), expression);
12310
        if (!o) obj.assign(self, o = {}, locals);
12311
        return o[key] = value;
12312
      }
12313
    });
12314
  },
12315
 
12316
  functionCall: function(fnGetter, contextGetter) {
12317
    var argsFn = [];
12318
    if (this.peekToken().text !== ')') {
12319
      do {
12320
        argsFn.push(this.expression());
12321
      } while (this.expect(','));
12322
    }
12323
    this.consume(')');
12324
 
12325
    var expressionText = this.text;
12326
    // we can safely reuse the array across invocations
12327
    var args = argsFn.length ? [] : null;
12328
 
12329
    return function $parseFunctionCall(scope, locals) {
12330
      var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
12331
      var fn = fnGetter(scope, locals, context) || noop;
12332
 
12333
      if (args) {
12334
        var i = argsFn.length;
12335
        while (i--) {
12336
          args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
12337
        }
12338
      }
12339
 
12340
      ensureSafeObject(context, expressionText);
12341
      ensureSafeFunction(fn, expressionText);
12342
 
12343
      // IE doesn't have apply for some native functions
12344
      var v = fn.apply
12345
            ? fn.apply(context, args)
12346
            : fn(args[0], args[1], args[2], args[3], args[4]);
12347
 
12348
      if (args) {
12349
        // Free-up the memory (arguments of the last function call).
12350
        args.length = 0;
12351
      }
12352
 
12353
      return ensureSafeObject(v, expressionText);
12354
      };
12355
  },
12356
 
12357
  // This is used with json array declaration
12358
  arrayDeclaration: function() {
12359
    var elementFns = [];
12360
    if (this.peekToken().text !== ']') {
12361
      do {
12362
        if (this.peek(']')) {
12363
          // Support trailing commas per ES5.1.
12364
          break;
12365
        }
12366
        elementFns.push(this.expression());
12367
      } while (this.expect(','));
12368
    }
12369
    this.consume(']');
12370
 
12371
    return extend(function $parseArrayLiteral(self, locals) {
12372
      var array = [];
12373
      for (var i = 0, ii = elementFns.length; i < ii; i++) {
12374
        array.push(elementFns[i](self, locals));
12375
      }
12376
      return array;
12377
    }, {
12378
      literal: true,
12379
      constant: elementFns.every(isConstant),
12380
      inputs: elementFns
12381
    });
12382
  },
12383
 
12384
  object: function() {
12385
    var keys = [], valueFns = [];
12386
    if (this.peekToken().text !== '}') {
12387
      do {
12388
        if (this.peek('}')) {
12389
          // Support trailing commas per ES5.1.
12390
          break;
12391
        }
12392
        var token = this.consume();
12393
        if (token.constant) {
12394
          keys.push(token.value);
12395
        } else if (token.identifier) {
12396
          keys.push(token.text);
12397
        } else {
12398
          this.throwError("invalid key", token);
12399
        }
12400
        this.consume(':');
12401
        valueFns.push(this.expression());
12402
      } while (this.expect(','));
12403
    }
12404
    this.consume('}');
12405
 
12406
    return extend(function $parseObjectLiteral(self, locals) {
12407
      var object = {};
12408
      for (var i = 0, ii = valueFns.length; i < ii; i++) {
12409
        object[keys[i]] = valueFns[i](self, locals);
12410
      }
12411
      return object;
12412
    }, {
12413
      literal: true,
12414
      constant: valueFns.every(isConstant),
12415
      inputs: valueFns
12416
    });
12417
  }
12418
};
12419
 
12420
 
12421
//////////////////////////////////////////////////
12422
// Parser helper functions
12423
//////////////////////////////////////////////////
12424
 
12425
function setter(obj, locals, path, setValue, fullExp) {
12426
  ensureSafeObject(obj, fullExp);
12427
  ensureSafeObject(locals, fullExp);
12428
 
12429
  var element = path.split('.'), key;
12430
  for (var i = 0; element.length > 1; i++) {
12431
    key = ensureSafeMemberName(element.shift(), fullExp);
12432
    var propertyObj = (i === 0 && locals && locals[key]) || obj[key];
12433
    if (!propertyObj) {
12434
      propertyObj = {};
12435
      obj[key] = propertyObj;
12436
    }
12437
    obj = ensureSafeObject(propertyObj, fullExp);
12438
  }
12439
  key = ensureSafeMemberName(element.shift(), fullExp);
12440
  ensureSafeObject(obj[key], fullExp);
12441
  obj[key] = setValue;
12442
  return setValue;
12443
}
12444
 
12445
var getterFnCacheDefault = createMap();
12446
var getterFnCacheExpensive = createMap();
12447
 
12448
function isPossiblyDangerousMemberName(name) {
12449
  return name == 'constructor';
12450
}
12451
 
12452
/**
12453
 * Implementation of the "Black Hole" variant from:
12454
 * - http://jsperf.com/angularjs-parse-getter/4
12455
 * - http://jsperf.com/path-evaluation-simplified/7
12456
 */
12457
function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
12458
  ensureSafeMemberName(key0, fullExp);
12459
  ensureSafeMemberName(key1, fullExp);
12460
  ensureSafeMemberName(key2, fullExp);
12461
  ensureSafeMemberName(key3, fullExp);
12462
  ensureSafeMemberName(key4, fullExp);
12463
  var eso = function(o) {
12464
    return ensureSafeObject(o, fullExp);
12465
  };
12466
  var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
12467
  var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
12468
  var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
12469
  var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
12470
  var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
12471
 
12472
  return function cspSafeGetter(scope, locals) {
12473
    var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
12474
 
12475
    if (pathVal == null) return pathVal;
12476
    pathVal = eso0(pathVal[key0]);
12477
 
12478
    if (!key1) return pathVal;
12479
    if (pathVal == null) return undefined;
12480
    pathVal = eso1(pathVal[key1]);
12481
 
12482
    if (!key2) return pathVal;
12483
    if (pathVal == null) return undefined;
12484
    pathVal = eso2(pathVal[key2]);
12485
 
12486
    if (!key3) return pathVal;
12487
    if (pathVal == null) return undefined;
12488
    pathVal = eso3(pathVal[key3]);
12489
 
12490
    if (!key4) return pathVal;
12491
    if (pathVal == null) return undefined;
12492
    pathVal = eso4(pathVal[key4]);
12493
 
12494
    return pathVal;
12495
  };
12496
}
12497
 
12498
function getterFnWithEnsureSafeObject(fn, fullExpression) {
12499
  return function(s, l) {
12500
    return fn(s, l, ensureSafeObject, fullExpression);
12501
  };
12502
}
12503
 
12504
function getterFn(path, options, fullExp) {
12505
  var expensiveChecks = options.expensiveChecks;
12506
  var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
12507
  var fn = getterFnCache[path];
12508
  if (fn) return fn;
12509
 
12510
 
12511
  var pathKeys = path.split('.'),
12512
      pathKeysLength = pathKeys.length;
12513
 
12514
  // http://jsperf.com/angularjs-parse-getter/6
12515
  if (options.csp) {
12516
    if (pathKeysLength < 6) {
12517
      fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
12518
    } else {
12519
      fn = function cspSafeGetter(scope, locals) {
12520
        var i = 0, val;
12521
        do {
12522
          val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
12523
                                pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
12524
 
12525
          locals = undefined; // clear after first iteration
12526
          scope = val;
12527
        } while (i < pathKeysLength);
12528
        return val;
12529
      };
12530
    }
12531
  } else {
12532
    var code = '';
12533
    if (expensiveChecks) {
12534
      code += 's = eso(s, fe);\nl = eso(l, fe);\n';
12535
    }
12536
    var needsEnsureSafeObject = expensiveChecks;
12537
    forEach(pathKeys, function(key, index) {
12538
      ensureSafeMemberName(key, fullExp);
12539
      var lookupJs = (index
12540
                      // we simply dereference 's' on any .dot notation
12541
                      ? 's'
12542
                      // but if we are first then we check locals first, and if so read it first
12543
                      : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
12544
      if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
12545
        lookupJs = 'eso(' + lookupJs + ', fe)';
12546
        needsEnsureSafeObject = true;
12547
      }
12548
      code += 'if(s == null) return undefined;\n' +
12549
              's=' + lookupJs + ';\n';
12550
    });
12551
    code += 'return s;';
12552
 
12553
    /* jshint -W054 */
12554
    var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
12555
    /* jshint +W054 */
12556
    evaledFnGetter.toString = valueFn(code);
12557
    if (needsEnsureSafeObject) {
12558
      evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
12559
    }
12560
    fn = evaledFnGetter;
12561
  }
12562
 
12563
  fn.sharedGetter = true;
12564
  fn.assign = function(self, value, locals) {
12565
    return setter(self, locals, path, value, path);
12566
  };
12567
  getterFnCache[path] = fn;
12568
  return fn;
12569
}
12570
 
12571
var objectValueOf = Object.prototype.valueOf;
12572
 
12573
function getValueOf(value) {
12574
  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
12575
}
12576
 
12577
///////////////////////////////////
12578
 
12579
/**
12580
 * @ngdoc service
12581
 * @name $parse
12582
 * @kind function
12583
 *
12584
 * @description
12585
 *
12586
 * Converts Angular {@link guide/expression expression} into a function.
12587
 *
12588
 * ```js
12589
 *   var getter = $parse('user.name');
12590
 *   var setter = getter.assign;
12591
 *   var context = {user:{name:'angular'}};
12592
 *   var locals = {user:{name:'local'}};
12593
 *
12594
 *   expect(getter(context)).toEqual('angular');
12595
 *   setter(context, 'newValue');
12596
 *   expect(context.user.name).toEqual('newValue');
12597
 *   expect(getter(context, locals)).toEqual('local');
12598
 * ```
12599
 *
12600
 *
12601
 * @param {string} expression String expression to compile.
12602
 * @returns {function(context, locals)} a function which represents the compiled expression:
12603
 *
12604
 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
12605
 *      are evaluated against (typically a scope object).
12606
 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
12607
 *      `context`.
12608
 *
12609
 *    The returned function also has the following properties:
12610
 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
12611
 *        literal.
12612
 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
12613
 *        constant literals.
12614
 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
12615
 *        set to a function to change its value on the given context.
12616
 *
12617
 */
12618
 
12619
 
12620
/**
12621
 * @ngdoc provider
12622
 * @name $parseProvider
12623
 *
12624
 * @description
12625
 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
12626
 *  service.
12627
 */
12628
function $ParseProvider() {
12629
  var cacheDefault = createMap();
12630
  var cacheExpensive = createMap();
12631
 
12632
 
12633
 
12634
  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
12635
    var $parseOptions = {
12636
          csp: $sniffer.csp,
12637
          expensiveChecks: false
12638
        },
12639
        $parseOptionsExpensive = {
12640
          csp: $sniffer.csp,
12641
          expensiveChecks: true
12642
        };
12643
 
12644
    function wrapSharedExpression(exp) {
12645
      var wrapped = exp;
12646
 
12647
      if (exp.sharedGetter) {
12648
        wrapped = function $parseWrapper(self, locals) {
12649
          return exp(self, locals);
12650
        };
12651
        wrapped.literal = exp.literal;
12652
        wrapped.constant = exp.constant;
12653
        wrapped.assign = exp.assign;
12654
      }
12655
 
12656
      return wrapped;
12657
    }
12658
 
12659
    return function $parse(exp, interceptorFn, expensiveChecks) {
12660
      var parsedExpression, oneTime, cacheKey;
12661
 
12662
      switch (typeof exp) {
12663
        case 'string':
12664
          cacheKey = exp = exp.trim();
12665
 
12666
          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
12667
          parsedExpression = cache[cacheKey];
12668
 
12669
          if (!parsedExpression) {
12670
            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
12671
              oneTime = true;
12672
              exp = exp.substring(2);
12673
            }
12674
 
12675
            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
12676
            var lexer = new Lexer(parseOptions);
12677
            var parser = new Parser(lexer, $filter, parseOptions);
12678
            parsedExpression = parser.parse(exp);
12679
 
12680
            if (parsedExpression.constant) {
12681
              parsedExpression.$$watchDelegate = constantWatchDelegate;
12682
            } else if (oneTime) {
12683
              //oneTime is not part of the exp passed to the Parser so we may have to
12684
              //wrap the parsedExpression before adding a $$watchDelegate
12685
              parsedExpression = wrapSharedExpression(parsedExpression);
12686
              parsedExpression.$$watchDelegate = parsedExpression.literal ?
12687
                oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
12688
            } else if (parsedExpression.inputs) {
12689
              parsedExpression.$$watchDelegate = inputsWatchDelegate;
12690
            }
12691
 
12692
            cache[cacheKey] = parsedExpression;
12693
          }
12694
          return addInterceptor(parsedExpression, interceptorFn);
12695
 
12696
        case 'function':
12697
          return addInterceptor(exp, interceptorFn);
12698
 
12699
        default:
12700
          return addInterceptor(noop, interceptorFn);
12701
      }
12702
    };
12703
 
12704
    function collectExpressionInputs(inputs, list) {
12705
      for (var i = 0, ii = inputs.length; i < ii; i++) {
12706
        var input = inputs[i];
12707
        if (!input.constant) {
12708
          if (input.inputs) {
12709
            collectExpressionInputs(input.inputs, list);
12710
          } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?
12711
            list.push(input);
12712
          }
12713
        }
12714
      }
12715
 
12716
      return list;
12717
    }
12718
 
12719
    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
12720
 
12721
      if (newValue == null || oldValueOfValue == null) { // null/undefined
12722
        return newValue === oldValueOfValue;
12723
      }
12724
 
12725
      if (typeof newValue === 'object') {
12726
 
12727
        // attempt to convert the value to a primitive type
12728
        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
12729
        //             be cheaply dirty-checked
12730
        newValue = getValueOf(newValue);
12731
 
12732
        if (typeof newValue === 'object') {
12733
          // objects/arrays are not supported - deep-watching them would be too expensive
12734
          return false;
12735
        }
12736
 
12737
        // fall-through to the primitive equality check
12738
      }
12739
 
12740
      //Primitive or NaN
12741
      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
12742
    }
12743
 
12744
    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12745
      var inputExpressions = parsedExpression.$$inputs ||
12746
                    (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));
12747
 
12748
      var lastResult;
12749
 
12750
      if (inputExpressions.length === 1) {
12751
        var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails
12752
        inputExpressions = inputExpressions[0];
12753
        return scope.$watch(function expressionInputWatch(scope) {
12754
          var newInputValue = inputExpressions(scope);
12755
          if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
12756
            lastResult = parsedExpression(scope);
12757
            oldInputValue = newInputValue && getValueOf(newInputValue);
12758
          }
12759
          return lastResult;
12760
        }, listener, objectEquality);
12761
      }
12762
 
12763
      var oldInputValueOfValues = [];
12764
      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12765
        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
12766
      }
12767
 
12768
      return scope.$watch(function expressionInputsWatch(scope) {
12769
        var changed = false;
12770
 
12771
        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12772
          var newInputValue = inputExpressions[i](scope);
12773
          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
12774
            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
12775
          }
12776
        }
12777
 
12778
        if (changed) {
12779
          lastResult = parsedExpression(scope);
12780
        }
12781
 
12782
        return lastResult;
12783
      }, listener, objectEquality);
12784
    }
12785
 
12786
    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12787
      var unwatch, lastValue;
12788
      return unwatch = scope.$watch(function oneTimeWatch(scope) {
12789
        return parsedExpression(scope);
12790
      }, function oneTimeListener(value, old, scope) {
12791
        lastValue = value;
12792
        if (isFunction(listener)) {
12793
          listener.apply(this, arguments);
12794
        }
12795
        if (isDefined(value)) {
12796
          scope.$$postDigest(function() {
12797
            if (isDefined(lastValue)) {
12798
              unwatch();
12799
            }
12800
          });
12801
        }
12802
      }, objectEquality);
12803
    }
12804
 
12805
    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12806
      var unwatch, lastValue;
12807
      return unwatch = scope.$watch(function oneTimeWatch(scope) {
12808
        return parsedExpression(scope);
12809
      }, function oneTimeListener(value, old, scope) {
12810
        lastValue = value;
12811
        if (isFunction(listener)) {
12812
          listener.call(this, value, old, scope);
12813
        }
12814
        if (isAllDefined(value)) {
12815
          scope.$$postDigest(function() {
12816
            if (isAllDefined(lastValue)) unwatch();
12817
          });
12818
        }
12819
      }, objectEquality);
12820
 
12821
      function isAllDefined(value) {
12822
        var allDefined = true;
12823
        forEach(value, function(val) {
12824
          if (!isDefined(val)) allDefined = false;
12825
        });
12826
        return allDefined;
12827
      }
12828
    }
12829
 
12830
    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12831
      var unwatch;
12832
      return unwatch = scope.$watch(function constantWatch(scope) {
12833
        return parsedExpression(scope);
12834
      }, function constantListener(value, old, scope) {
12835
        if (isFunction(listener)) {
12836
          listener.apply(this, arguments);
12837
        }
12838
        unwatch();
12839
      }, objectEquality);
12840
    }
12841
 
12842
    function addInterceptor(parsedExpression, interceptorFn) {
12843
      if (!interceptorFn) return parsedExpression;
12844
      var watchDelegate = parsedExpression.$$watchDelegate;
12845
 
12846
      var regularWatch =
12847
          watchDelegate !== oneTimeLiteralWatchDelegate &&
12848
          watchDelegate !== oneTimeWatchDelegate;
12849
 
12850
      var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12851
        var value = parsedExpression(scope, locals);
12852
        return interceptorFn(value, scope, locals);
12853
      } : function oneTimeInterceptedExpression(scope, locals) {
12854
        var value = parsedExpression(scope, locals);
12855
        var result = interceptorFn(value, scope, locals);
12856
        // we only return the interceptor's result if the
12857
        // initial value is defined (for bind-once)
12858
        return isDefined(value) ? result : value;
12859
      };
12860
 
12861
      // Propagate $$watchDelegates other then inputsWatchDelegate
12862
      if (parsedExpression.$$watchDelegate &&
12863
          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
12864
        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
12865
      } else if (!interceptorFn.$stateful) {
12866
        // If there is an interceptor, but no watchDelegate then treat the interceptor like
12867
        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
12868
        fn.$$watchDelegate = inputsWatchDelegate;
12869
        fn.inputs = [parsedExpression];
12870
      }
12871
 
12872
      return fn;
12873
    }
12874
  }];
12875
}
12876
 
12877
/**
12878
 * @ngdoc service
12879
 * @name $q
12880
 * @requires $rootScope
12881
 *
12882
 * @description
12883
 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
12884
 * when they are done processing.
12885
 *
12886
 * This is an implementation of promises/deferred objects inspired by
12887
 * [Kris Kowal's Q](https://github.com/kriskowal/q).
12888
 *
12889
 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
12890
 * implementations, and the other which resembles ES6 promises to some degree.
12891
 *
12892
 * # $q constructor
12893
 *
12894
 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
12895
 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
12896
 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
12897
 *
12898
 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
12899
 * available yet.
12900
 *
12901
 * It can be used like so:
12902
 *
12903
 * ```js
12904
 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12905
 *   // are available in the current lexical scope (they could have been injected or passed in).
12906
 *
12907
 *   function asyncGreet(name) {
12908
 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
12909
 *     return $q(function(resolve, reject) {
12910
 *       setTimeout(function() {
12911
 *         if (okToGreet(name)) {
12912
 *           resolve('Hello, ' + name + '!');
12913
 *         } else {
12914
 *           reject('Greeting ' + name + ' is not allowed.');
12915
 *         }
12916
 *       }, 1000);
12917
 *     });
12918
 *   }
12919
 *
12920
 *   var promise = asyncGreet('Robin Hood');
12921
 *   promise.then(function(greeting) {
12922
 *     alert('Success: ' + greeting);
12923
 *   }, function(reason) {
12924
 *     alert('Failed: ' + reason);
12925
 *   });
12926
 * ```
12927
 *
12928
 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
12929
 *
12930
 * However, the more traditional CommonJS-style usage is still available, and documented below.
12931
 *
12932
 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
12933
 * interface for interacting with an object that represents the result of an action that is
12934
 * performed asynchronously, and may or may not be finished at any given point in time.
12935
 *
12936
 * From the perspective of dealing with error handling, deferred and promise APIs are to
12937
 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
12938
 *
12939
 * ```js
12940
 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12941
 *   // are available in the current lexical scope (they could have been injected or passed in).
12942
 *
12943
 *   function asyncGreet(name) {
12944
 *     var deferred = $q.defer();
12945
 *
12946
 *     setTimeout(function() {
12947
 *       deferred.notify('About to greet ' + name + '.');
12948
 *
12949
 *       if (okToGreet(name)) {
12950
 *         deferred.resolve('Hello, ' + name + '!');
12951
 *       } else {
12952
 *         deferred.reject('Greeting ' + name + ' is not allowed.');
12953
 *       }
12954
 *     }, 1000);
12955
 *
12956
 *     return deferred.promise;
12957
 *   }
12958
 *
12959
 *   var promise = asyncGreet('Robin Hood');
12960
 *   promise.then(function(greeting) {
12961
 *     alert('Success: ' + greeting);
12962
 *   }, function(reason) {
12963
 *     alert('Failed: ' + reason);
12964
 *   }, function(update) {
12965
 *     alert('Got notification: ' + update);
12966
 *   });
12967
 * ```
12968
 *
12969
 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
12970
 * comes in the way of guarantees that promise and deferred APIs make, see
12971
 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
12972
 *
12973
 * Additionally the promise api allows for composition that is very hard to do with the
12974
 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
12975
 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
12976
 * section on serial or parallel joining of promises.
12977
 *
12978
 * # The Deferred API
12979
 *
12980
 * A new instance of deferred is constructed by calling `$q.defer()`.
12981
 *
12982
 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
12983
 * that can be used for signaling the successful or unsuccessful completion, as well as the status
12984
 * of the task.
12985
 *
12986
 * **Methods**
12987
 *
12988
 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
12989
 *   constructed via `$q.reject`, the promise will be rejected instead.
12990
 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
12991
 *   resolving it with a rejection constructed via `$q.reject`.
12992
 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
12993
 *   multiple times before the promise is either resolved or rejected.
12994
 *
12995
 * **Properties**
12996
 *
12997
 * - promise – `{Promise}` – promise object associated with this deferred.
12998
 *
12999
 *
13000
 * # The Promise API
13001
 *
13002
 * A new promise instance is created when a deferred instance is created and can be retrieved by
13003
 * calling `deferred.promise`.
13004
 *
13005
 * The purpose of the promise object is to allow for interested parties to get access to the result
13006
 * of the deferred task when it completes.
13007
 *
13008
 * **Methods**
13009
 *
13010
 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
13011
 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
13012
 *   as soon as the result is available. The callbacks are called with a single argument: the result
13013
 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
13014
 *   provide a progress indication, before the promise is resolved or rejected.
13015
 *
13016
 *   This method *returns a new promise* which is resolved or rejected via the return value of the
13017
 *   `successCallback`, `errorCallback`. It also notifies via the return value of the
13018
 *   `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback
13019
 *   method.
13020
 *
13021
 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
13022
 *
13023
 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
13024
 *   but to do so without modifying the final value. This is useful to release resources or do some
13025
 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
13026
 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
13027
 *   more information.
13028
 *
13029
 * # Chaining promises
13030
 *
13031
 * Because calling the `then` method of a promise returns a new derived promise, it is easily
13032
 * possible to create a chain of promises:
13033
 *
13034
 * ```js
13035
 *   promiseB = promiseA.then(function(result) {
13036
 *     return result + 1;
13037
 *   });
13038
 *
13039
 *   // promiseB will be resolved immediately after promiseA is resolved and its value
13040
 *   // will be the result of promiseA incremented by 1
13041
 * ```
13042
 *
13043
 * It is possible to create chains of any length and since a promise can be resolved with another
13044
 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
13045
 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
13046
 * $http's response interceptors.
13047
 *
13048
 *
13049
 * # Differences between Kris Kowal's Q and $q
13050
 *
13051
 *  There are two main differences:
13052
 *
13053
 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
13054
 *   mechanism in angular, which means faster propagation of resolution or rejection into your
13055
 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
13056
 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
13057
 *   all the important functionality needed for common async tasks.
13058
 *
13059
 *  # Testing
13060
 *
13061
 *  ```js
13062
 *    it('should simulate promise', inject(function($q, $rootScope) {
13063
 *      var deferred = $q.defer();
13064
 *      var promise = deferred.promise;
13065
 *      var resolvedValue;
13066
 *
13067
 *      promise.then(function(value) { resolvedValue = value; });
13068
 *      expect(resolvedValue).toBeUndefined();
13069
 *
13070
 *      // Simulate resolving of promise
13071
 *      deferred.resolve(123);
13072
 *      // Note that the 'then' function does not get called synchronously.
13073
 *      // This is because we want the promise API to always be async, whether or not
13074
 *      // it got called synchronously or asynchronously.
13075
 *      expect(resolvedValue).toBeUndefined();
13076
 *
13077
 *      // Propagate promise resolution to 'then' functions using $apply().
13078
 *      $rootScope.$apply();
13079
 *      expect(resolvedValue).toEqual(123);
13080
 *    }));
13081
 *  ```
13082
 *
13083
 * @param {function(function, function)} resolver Function which is responsible for resolving or
13084
 *   rejecting the newly created promise. The first parameter is a function which resolves the
13085
 *   promise, the second parameter is a function which rejects the promise.
13086
 *
13087
 * @returns {Promise} The newly created promise.
13088
 */
13089
function $QProvider() {
13090
 
13091
  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
13092
    return qFactory(function(callback) {
13093
      $rootScope.$evalAsync(callback);
13094
    }, $exceptionHandler);
13095
  }];
13096
}
13097
 
13098
function $$QProvider() {
13099
  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
13100
    return qFactory(function(callback) {
13101
      $browser.defer(callback);
13102
    }, $exceptionHandler);
13103
  }];
13104
}
13105
 
13106
/**
13107
 * Constructs a promise manager.
13108
 *
13109
 * @param {function(function)} nextTick Function for executing functions in the next turn.
13110
 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
13111
 *     debugging purposes.
13112
 * @returns {object} Promise manager.
13113
 */
13114
function qFactory(nextTick, exceptionHandler) {
13115
  var $qMinErr = minErr('$q', TypeError);
13116
  function callOnce(self, resolveFn, rejectFn) {
13117
    var called = false;
13118
    function wrap(fn) {
13119
      return function(value) {
13120
        if (called) return;
13121
        called = true;
13122
        fn.call(self, value);
13123
      };
13124
    }
13125
 
13126
    return [wrap(resolveFn), wrap(rejectFn)];
13127
  }
13128
 
13129
  /**
13130
   * @ngdoc method
13131
   * @name ng.$q#defer
13132
   * @kind function
13133
   *
13134
   * @description
13135
   * Creates a `Deferred` object which represents a task which will finish in the future.
13136
   *
13137
   * @returns {Deferred} Returns a new instance of deferred.
13138
   */
13139
  var defer = function() {
13140
    return new Deferred();
13141
  };
13142
 
13143
  function Promise() {
13144
    this.$$state = { status: 0 };
13145
  }
13146
 
13147
  Promise.prototype = {
13148
    then: function(onFulfilled, onRejected, progressBack) {
13149
      var result = new Deferred();
13150
 
13151
      this.$$state.pending = this.$$state.pending || [];
13152
      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
13153
      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
13154
 
13155
      return result.promise;
13156
    },
13157
 
13158
    "catch": function(callback) {
13159
      return this.then(null, callback);
13160
    },
13161
 
13162
    "finally": function(callback, progressBack) {
13163
      return this.then(function(value) {
13164
        return handleCallback(value, true, callback);
13165
      }, function(error) {
13166
        return handleCallback(error, false, callback);
13167
      }, progressBack);
13168
    }
13169
  };
13170
 
13171
  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
13172
  function simpleBind(context, fn) {
13173
    return function(value) {
13174
      fn.call(context, value);
13175
    };
13176
  }
13177
 
13178
  function processQueue(state) {
13179
    var fn, promise, pending;
13180
 
13181
    pending = state.pending;
13182
    state.processScheduled = false;
13183
    state.pending = undefined;
13184
    for (var i = 0, ii = pending.length; i < ii; ++i) {
13185
      promise = pending[i][0];
13186
      fn = pending[i][state.status];
13187
      try {
13188
        if (isFunction(fn)) {
13189
          promise.resolve(fn(state.value));
13190
        } else if (state.status === 1) {
13191
          promise.resolve(state.value);
13192
        } else {
13193
          promise.reject(state.value);
13194
        }
13195
      } catch (e) {
13196
        promise.reject(e);
13197
        exceptionHandler(e);
13198
      }
13199
    }
13200
  }
13201
 
13202
  function scheduleProcessQueue(state) {
13203
    if (state.processScheduled || !state.pending) return;
13204
    state.processScheduled = true;
13205
    nextTick(function() { processQueue(state); });
13206
  }
13207
 
13208
  function Deferred() {
13209
    this.promise = new Promise();
13210
    //Necessary to support unbound execution :/
13211
    this.resolve = simpleBind(this, this.resolve);
13212
    this.reject = simpleBind(this, this.reject);
13213
    this.notify = simpleBind(this, this.notify);
13214
  }
13215
 
13216
  Deferred.prototype = {
13217
    resolve: function(val) {
13218
      if (this.promise.$$state.status) return;
13219
      if (val === this.promise) {
13220
        this.$$reject($qMinErr(
13221
          'qcycle',
13222
          "Expected promise to be resolved with value other than itself '{0}'",
13223
          val));
13224
      } else {
13225
        this.$$resolve(val);
13226
      }
13227
 
13228
    },
13229
 
13230
    $$resolve: function(val) {
13231
      var then, fns;
13232
 
13233
      fns = callOnce(this, this.$$resolve, this.$$reject);
13234
      try {
13235
        if ((isObject(val) || isFunction(val))) then = val && val.then;
13236
        if (isFunction(then)) {
13237
          this.promise.$$state.status = -1;
13238
          then.call(val, fns[0], fns[1], this.notify);
13239
        } else {
13240
          this.promise.$$state.value = val;
13241
          this.promise.$$state.status = 1;
13242
          scheduleProcessQueue(this.promise.$$state);
13243
        }
13244
      } catch (e) {
13245
        fns[1](e);
13246
        exceptionHandler(e);
13247
      }
13248
    },
13249
 
13250
    reject: function(reason) {
13251
      if (this.promise.$$state.status) return;
13252
      this.$$reject(reason);
13253
    },
13254
 
13255
    $$reject: function(reason) {
13256
      this.promise.$$state.value = reason;
13257
      this.promise.$$state.status = 2;
13258
      scheduleProcessQueue(this.promise.$$state);
13259
    },
13260
 
13261
    notify: function(progress) {
13262
      var callbacks = this.promise.$$state.pending;
13263
 
13264
      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
13265
        nextTick(function() {
13266
          var callback, result;
13267
          for (var i = 0, ii = callbacks.length; i < ii; i++) {
13268
            result = callbacks[i][0];
13269
            callback = callbacks[i][3];
13270
            try {
13271
              result.notify(isFunction(callback) ? callback(progress) : progress);
13272
            } catch (e) {
13273
              exceptionHandler(e);
13274
            }
13275
          }
13276
        });
13277
      }
13278
    }
13279
  };
13280
 
13281
  /**
13282
   * @ngdoc method
13283
   * @name $q#reject
13284
   * @kind function
13285
   *
13286
   * @description
13287
   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
13288
   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
13289
   * a promise chain, you don't need to worry about it.
13290
   *
13291
   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
13292
   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
13293
   * a promise error callback and you want to forward the error to the promise derived from the
13294
   * current promise, you have to "rethrow" the error by returning a rejection constructed via
13295
   * `reject`.
13296
   *
13297
   * ```js
13298
   *   promiseB = promiseA.then(function(result) {
13299
   *     // success: do something and resolve promiseB
13300
   *     //          with the old or a new result
13301
   *     return result;
13302
   *   }, function(reason) {
13303
   *     // error: handle the error if possible and
13304
   *     //        resolve promiseB with newPromiseOrValue,
13305
   *     //        otherwise forward the rejection to promiseB
13306
   *     if (canHandle(reason)) {
13307
   *      // handle the error and recover
13308
   *      return newPromiseOrValue;
13309
   *     }
13310
   *     return $q.reject(reason);
13311
   *   });
13312
   * ```
13313
   *
13314
   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
13315
   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
13316
   */
13317
  var reject = function(reason) {
13318
    var result = new Deferred();
13319
    result.reject(reason);
13320
    return result.promise;
13321
  };
13322
 
13323
  var makePromise = function makePromise(value, resolved) {
13324
    var result = new Deferred();
13325
    if (resolved) {
13326
      result.resolve(value);
13327
    } else {
13328
      result.reject(value);
13329
    }
13330
    return result.promise;
13331
  };
13332
 
13333
  var handleCallback = function handleCallback(value, isResolved, callback) {
13334
    var callbackOutput = null;
13335
    try {
13336
      if (isFunction(callback)) callbackOutput = callback();
13337
    } catch (e) {
13338
      return makePromise(e, false);
13339
    }
13340
    if (isPromiseLike(callbackOutput)) {
13341
      return callbackOutput.then(function() {
13342
        return makePromise(value, isResolved);
13343
      }, function(error) {
13344
        return makePromise(error, false);
13345
      });
13346
    } else {
13347
      return makePromise(value, isResolved);
13348
    }
13349
  };
13350
 
13351
  /**
13352
   * @ngdoc method
13353
   * @name $q#when
13354
   * @kind function
13355
   *
13356
   * @description
13357
   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
13358
   * This is useful when you are dealing with an object that might or might not be a promise, or if
13359
   * the promise comes from a source that can't be trusted.
13360
   *
13361
   * @param {*} value Value or a promise
13362
   * @returns {Promise} Returns a promise of the passed value or promise
13363
   */
13364
 
13365
 
13366
  var when = function(value, callback, errback, progressBack) {
13367
    var result = new Deferred();
13368
    result.resolve(value);
13369
    return result.promise.then(callback, errback, progressBack);
13370
  };
13371
 
13372
  /**
13373
   * @ngdoc method
13374
   * @name $q#all
13375
   * @kind function
13376
   *
13377
   * @description
13378
   * Combines multiple promises into a single promise that is resolved when all of the input
13379
   * promises are resolved.
13380
   *
13381
   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
13382
   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
13383
   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
13384
   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
13385
   *   with the same rejection value.
13386
   */
13387
 
13388
  function all(promises) {
13389
    var deferred = new Deferred(),
13390
        counter = 0,
13391
        results = isArray(promises) ? [] : {};
13392
 
13393
    forEach(promises, function(promise, key) {
13394
      counter++;
13395
      when(promise).then(function(value) {
13396
        if (results.hasOwnProperty(key)) return;
13397
        results[key] = value;
13398
        if (!(--counter)) deferred.resolve(results);
13399
      }, function(reason) {
13400
        if (results.hasOwnProperty(key)) return;
13401
        deferred.reject(reason);
13402
      });
13403
    });
13404
 
13405
    if (counter === 0) {
13406
      deferred.resolve(results);
13407
    }
13408
 
13409
    return deferred.promise;
13410
  }
13411
 
13412
  var $Q = function Q(resolver) {
13413
    if (!isFunction(resolver)) {
13414
      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
13415
    }
13416
 
13417
    if (!(this instanceof Q)) {
13418
      // More useful when $Q is the Promise itself.
13419
      return new Q(resolver);
13420
    }
13421
 
13422
    var deferred = new Deferred();
13423
 
13424
    function resolveFn(value) {
13425
      deferred.resolve(value);
13426
    }
13427
 
13428
    function rejectFn(reason) {
13429
      deferred.reject(reason);
13430
    }
13431
 
13432
    resolver(resolveFn, rejectFn);
13433
 
13434
    return deferred.promise;
13435
  };
13436
 
13437
  $Q.defer = defer;
13438
  $Q.reject = reject;
13439
  $Q.when = when;
13440
  $Q.all = all;
13441
 
13442
  return $Q;
13443
}
13444
 
13445
function $$RAFProvider() { //rAF
13446
  this.$get = ['$window', '$timeout', function($window, $timeout) {
13447
    var requestAnimationFrame = $window.requestAnimationFrame ||
13448
                                $window.webkitRequestAnimationFrame;
13449
 
13450
    var cancelAnimationFrame = $window.cancelAnimationFrame ||
13451
                               $window.webkitCancelAnimationFrame ||
13452
                               $window.webkitCancelRequestAnimationFrame;
13453
 
13454
    var rafSupported = !!requestAnimationFrame;
13455
    var raf = rafSupported
13456
      ? function(fn) {
13457
          var id = requestAnimationFrame(fn);
13458
          return function() {
13459
            cancelAnimationFrame(id);
13460
          };
13461
        }
13462
      : function(fn) {
13463
          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
13464
          return function() {
13465
            $timeout.cancel(timer);
13466
          };
13467
        };
13468
 
13469
    raf.supported = rafSupported;
13470
 
13471
    return raf;
13472
  }];
13473
}
13474
 
13475
/**
13476
 * DESIGN NOTES
13477
 *
13478
 * The design decisions behind the scope are heavily favored for speed and memory consumption.
13479
 *
13480
 * The typical use of scope is to watch the expressions, which most of the time return the same
13481
 * value as last time so we optimize the operation.
13482
 *
13483
 * Closures construction is expensive in terms of speed as well as memory:
13484
 *   - No closures, instead use prototypical inheritance for API
13485
 *   - Internal state needs to be stored on scope directly, which means that private state is
13486
 *     exposed as $$____ properties
13487
 *
13488
 * Loop operations are optimized by using while(count--) { ... }
13489
 *   - this means that in order to keep the same order of execution as addition we have to add
13490
 *     items to the array at the beginning (unshift) instead of at the end (push)
13491
 *
13492
 * Child scopes are created and removed often
13493
 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
13494
 *
13495
 * There are few watches then a lot of observers. This is why you don't want the observer to be
13496
 * implemented in the same way as watch. Watch requires return of initialization function which
13497
 * are expensive to construct.
13498
 */
13499
 
13500
 
13501
/**
13502
 * @ngdoc provider
13503
 * @name $rootScopeProvider
13504
 * @description
13505
 *
13506
 * Provider for the $rootScope service.
13507
 */
13508
 
13509
/**
13510
 * @ngdoc method
13511
 * @name $rootScopeProvider#digestTtl
13512
 * @description
13513
 *
13514
 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
13515
 * assuming that the model is unstable.
13516
 *
13517
 * The current default is 10 iterations.
13518
 *
13519
 * In complex applications it's possible that the dependencies between `$watch`s will result in
13520
 * several digest iterations. However if an application needs more than the default 10 digest
13521
 * iterations for its model to stabilize then you should investigate what is causing the model to
13522
 * continuously change during the digest.
13523
 *
13524
 * Increasing the TTL could have performance implications, so you should not change it without
13525
 * proper justification.
13526
 *
13527
 * @param {number} limit The number of digest iterations.
13528
 */
13529
 
13530
 
13531
/**
13532
 * @ngdoc service
13533
 * @name $rootScope
13534
 * @description
13535
 *
13536
 * Every application has a single root {@link ng.$rootScope.Scope scope}.
13537
 * All other scopes are descendant scopes of the root scope. Scopes provide separation
13538
 * between the model and the view, via a mechanism for watching the model for changes.
13539
 * They also provide an event emission/broadcast and subscription facility. See the
13540
 * {@link guide/scope developer guide on scopes}.
13541
 */
13542
function $RootScopeProvider() {
13543
  var TTL = 10;
13544
  var $rootScopeMinErr = minErr('$rootScope');
13545
  var lastDirtyWatch = null;
13546
  var applyAsyncId = null;
13547
 
13548
  this.digestTtl = function(value) {
13549
    if (arguments.length) {
13550
      TTL = value;
13551
    }
13552
    return TTL;
13553
  };
13554
 
13555
  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
13556
      function($injector, $exceptionHandler, $parse, $browser) {
13557
 
13558
    /**
13559
     * @ngdoc type
13560
     * @name $rootScope.Scope
13561
     *
13562
     * @description
13563
     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
13564
     * {@link auto.$injector $injector}. Child scopes are created using the
13565
     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
13566
     * compiled HTML template is executed.)
13567
     *
13568
     * Here is a simple scope snippet to show how you can interact with the scope.
13569
     * ```html
13570
     * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
13571
     * ```
13572
     *
13573
     * # Inheritance
13574
     * A scope can inherit from a parent scope, as in this example:
13575
     * ```js
13576
         var parent = $rootScope;
13577
         var child = parent.$new();
13578
 
13579
         parent.salutation = "Hello";
13580
         expect(child.salutation).toEqual('Hello');
13581
 
13582
         child.salutation = "Welcome";
13583
         expect(child.salutation).toEqual('Welcome');
13584
         expect(parent.salutation).toEqual('Hello');
13585
     * ```
13586
     *
13587
     * When interacting with `Scope` in tests, additional helper methods are available on the
13588
     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
13589
     * details.
13590
     *
13591
     *
13592
     * @param {Object.<string, function()>=} providers Map of service factory which need to be
13593
     *                                       provided for the current scope. Defaults to {@link ng}.
13594
     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
13595
     *                              append/override services provided by `providers`. This is handy
13596
     *                              when unit-testing and having the need to override a default
13597
     *                              service.
13598
     * @returns {Object} Newly created scope.
13599
     *
13600
     */
13601
    function Scope() {
13602
      this.$id = nextUid();
13603
      this.$$phase = this.$parent = this.$$watchers =
13604
                     this.$$nextSibling = this.$$prevSibling =
13605
                     this.$$childHead = this.$$childTail = null;
13606
      this.$root = this;
13607
      this.$$destroyed = false;
13608
      this.$$listeners = {};
13609
      this.$$listenerCount = {};
13610
      this.$$isolateBindings = null;
13611
    }
13612
 
13613
    /**
13614
     * @ngdoc property
13615
     * @name $rootScope.Scope#$id
13616
     *
13617
     * @description
13618
     * Unique scope ID (monotonically increasing) useful for debugging.
13619
     */
13620
 
13621
     /**
13622
      * @ngdoc property
13623
      * @name $rootScope.Scope#$parent
13624
      *
13625
      * @description
13626
      * Reference to the parent scope.
13627
      */
13628
 
13629
      /**
13630
       * @ngdoc property
13631
       * @name $rootScope.Scope#$root
13632
       *
13633
       * @description
13634
       * Reference to the root scope.
13635
       */
13636
 
13637
    Scope.prototype = {
13638
      constructor: Scope,
13639
      /**
13640
       * @ngdoc method
13641
       * @name $rootScope.Scope#$new
13642
       * @kind function
13643
       *
13644
       * @description
13645
       * Creates a new child {@link ng.$rootScope.Scope scope}.
13646
       *
13647
       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
13648
       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
13649
       *
13650
       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
13651
       * desired for the scope and its child scopes to be permanently detached from the parent and
13652
       * thus stop participating in model change detection and listener notification by invoking.
13653
       *
13654
       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
13655
       *         parent scope. The scope is isolated, as it can not see parent scope properties.
13656
       *         When creating widgets, it is useful for the widget to not accidentally read parent
13657
       *         state.
13658
       *
13659
       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
13660
       *                              of the newly created scope. Defaults to `this` scope if not provided.
13661
       *                              This is used when creating a transclude scope to correctly place it
13662
       *                              in the scope hierarchy while maintaining the correct prototypical
13663
       *                              inheritance.
13664
       *
13665
       * @returns {Object} The newly created child scope.
13666
       *
13667
       */
13668
      $new: function(isolate, parent) {
13669
        var child;
13670
 
13671
        parent = parent || this;
13672
 
13673
        if (isolate) {
13674
          child = new Scope();
13675
          child.$root = this.$root;
13676
        } else {
13677
          // Only create a child scope class if somebody asks for one,
13678
          // but cache it to allow the VM to optimize lookups.
13679
          if (!this.$$ChildScope) {
13680
            this.$$ChildScope = function ChildScope() {
13681
              this.$$watchers = this.$$nextSibling =
13682
                  this.$$childHead = this.$$childTail = null;
13683
              this.$$listeners = {};
13684
              this.$$listenerCount = {};
13685
              this.$id = nextUid();
13686
              this.$$ChildScope = null;
13687
            };
13688
            this.$$ChildScope.prototype = this;
13689
          }
13690
          child = new this.$$ChildScope();
13691
        }
13692
        child.$parent = parent;
13693
        child.$$prevSibling = parent.$$childTail;
13694
        if (parent.$$childHead) {
13695
          parent.$$childTail.$$nextSibling = child;
13696
          parent.$$childTail = child;
13697
        } else {
13698
          parent.$$childHead = parent.$$childTail = child;
13699
        }
13700
 
13701
        // When the new scope is not isolated or we inherit from `this`, and
13702
        // the parent scope is destroyed, the property `$$destroyed` is inherited
13703
        // prototypically. In all other cases, this property needs to be set
13704
        // when the parent scope is destroyed.
13705
        // The listener needs to be added after the parent is set
13706
        if (isolate || parent != this) child.$on('$destroy', destroyChild);
13707
 
13708
        return child;
13709
 
13710
        function destroyChild() {
13711
          child.$$destroyed = true;
13712
        }
13713
      },
13714
 
13715
      /**
13716
       * @ngdoc method
13717
       * @name $rootScope.Scope#$watch
13718
       * @kind function
13719
       *
13720
       * @description
13721
       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
13722
       *
13723
       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
13724
       *   $digest()} and should return the value that will be watched. (Since
13725
       *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
13726
       *   `watchExpression` can execute multiple times per
13727
       *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
13728
       * - The `listener` is called only when the value from the current `watchExpression` and the
13729
       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
13730
       *   see below). Inequality is determined according to reference inequality,
13731
       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
13732
       *    via the `!==` Javascript operator, unless `objectEquality == true`
13733
       *   (see next point)
13734
       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
13735
       *   according to the {@link angular.equals} function. To save the value of the object for
13736
       *   later comparison, the {@link angular.copy} function is used. This therefore means that
13737
       *   watching complex objects will have adverse memory and performance implications.
13738
       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
13739
       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
13740
       *   iteration limit is 10 to prevent an infinite loop deadlock.
13741
       *
13742
       *
13743
       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
13744
       * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
13745
       * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
13746
       * change is detected, be prepared for multiple calls to your listener.)
13747
       *
13748
       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
13749
       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
13750
       * watcher. In rare cases, this is undesirable because the listener is called when the result
13751
       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
13752
       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
13753
       * listener was called due to initialization.
13754
       *
13755
       *
13756
       *
13757
       * # Example
13758
       * ```js
13759
           // let's assume that scope was dependency injected as the $rootScope
13760
           var scope = $rootScope;
13761
           scope.name = 'misko';
13762
           scope.counter = 0;
13763
 
13764
           expect(scope.counter).toEqual(0);
13765
           scope.$watch('name', function(newValue, oldValue) {
13766
             scope.counter = scope.counter + 1;
13767
           });
13768
           expect(scope.counter).toEqual(0);
13769
 
13770
           scope.$digest();
13771
           // the listener is always called during the first $digest loop after it was registered
13772
           expect(scope.counter).toEqual(1);
13773
 
13774
           scope.$digest();
13775
           // but now it will not be called unless the value changes
13776
           expect(scope.counter).toEqual(1);
13777
 
13778
           scope.name = 'adam';
13779
           scope.$digest();
13780
           expect(scope.counter).toEqual(2);
13781
 
13782
 
13783
 
13784
           // Using a function as a watchExpression
13785
           var food;
13786
           scope.foodCounter = 0;
13787
           expect(scope.foodCounter).toEqual(0);
13788
           scope.$watch(
13789
             // This function returns the value being watched. It is called for each turn of the $digest loop
13790
             function() { return food; },
13791
             // This is the change listener, called when the value returned from the above function changes
13792
             function(newValue, oldValue) {
13793
               if ( newValue !== oldValue ) {
13794
                 // Only increment the counter if the value changed
13795
                 scope.foodCounter = scope.foodCounter + 1;
13796
               }
13797
             }
13798
           );
13799
           // No digest has been run so the counter will be zero
13800
           expect(scope.foodCounter).toEqual(0);
13801
 
13802
           // Run the digest but since food has not changed count will still be zero
13803
           scope.$digest();
13804
           expect(scope.foodCounter).toEqual(0);
13805
 
13806
           // Update food and run digest.  Now the counter will increment
13807
           food = 'cheeseburger';
13808
           scope.$digest();
13809
           expect(scope.foodCounter).toEqual(1);
13810
 
13811
       * ```
13812
       *
13813
       *
13814
       *
13815
       * @param {(function()|string)} watchExpression Expression that is evaluated on each
13816
       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
13817
       *    a call to the `listener`.
13818
       *
13819
       *    - `string`: Evaluated as {@link guide/expression expression}
13820
       *    - `function(scope)`: called with current `scope` as a parameter.
13821
       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
13822
       *    of `watchExpression` changes.
13823
       *
13824
       *    - `newVal` contains the current value of the `watchExpression`
13825
       *    - `oldVal` contains the previous value of the `watchExpression`
13826
       *    - `scope` refers to the current scope
13827
       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
13828
       *     comparing for reference equality.
13829
       * @returns {function()} Returns a deregistration function for this listener.
13830
       */
13831
      $watch: function(watchExp, listener, objectEquality) {
13832
        var get = $parse(watchExp);
13833
 
13834
        if (get.$$watchDelegate) {
13835
          return get.$$watchDelegate(this, listener, objectEquality, get);
13836
        }
13837
        var scope = this,
13838
            array = scope.$$watchers,
13839
            watcher = {
13840
              fn: listener,
13841
              last: initWatchVal,
13842
              get: get,
13843
              exp: watchExp,
13844
              eq: !!objectEquality
13845
            };
13846
 
13847
        lastDirtyWatch = null;
13848
 
13849
        if (!isFunction(listener)) {
13850
          watcher.fn = noop;
13851
        }
13852
 
13853
        if (!array) {
13854
          array = scope.$$watchers = [];
13855
        }
13856
        // we use unshift since we use a while loop in $digest for speed.
13857
        // the while loop reads in reverse order.
13858
        array.unshift(watcher);
13859
 
13860
        return function deregisterWatch() {
13861
          arrayRemove(array, watcher);
13862
          lastDirtyWatch = null;
13863
        };
13864
      },
13865
 
13866
      /**
13867
       * @ngdoc method
13868
       * @name $rootScope.Scope#$watchGroup
13869
       * @kind function
13870
       *
13871
       * @description
13872
       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
13873
       * If any one expression in the collection changes the `listener` is executed.
13874
       *
13875
       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
13876
       *   call to $digest() to see if any items changes.
13877
       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
13878
       *
13879
       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
13880
       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
13881
       *
13882
       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
13883
       *    expression in `watchExpressions` changes
13884
       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
13885
       *    those of `watchExpression`
13886
       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
13887
       *    those of `watchExpression`
13888
       *    The `scope` refers to the current scope.
13889
       * @returns {function()} Returns a de-registration function for all listeners.
13890
       */
13891
      $watchGroup: function(watchExpressions, listener) {
13892
        var oldValues = new Array(watchExpressions.length);
13893
        var newValues = new Array(watchExpressions.length);
13894
        var deregisterFns = [];
13895
        var self = this;
13896
        var changeReactionScheduled = false;
13897
        var firstRun = true;
13898
 
13899
        if (!watchExpressions.length) {
13900
          // No expressions means we call the listener ASAP
13901
          var shouldCall = true;
13902
          self.$evalAsync(function() {
13903
            if (shouldCall) listener(newValues, newValues, self);
13904
          });
13905
          return function deregisterWatchGroup() {
13906
            shouldCall = false;
13907
          };
13908
        }
13909
 
13910
        if (watchExpressions.length === 1) {
13911
          // Special case size of one
13912
          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
13913
            newValues[0] = value;
13914
            oldValues[0] = oldValue;
13915
            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
13916
          });
13917
        }
13918
 
13919
        forEach(watchExpressions, function(expr, i) {
13920
          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
13921
            newValues[i] = value;
13922
            oldValues[i] = oldValue;
13923
            if (!changeReactionScheduled) {
13924
              changeReactionScheduled = true;
13925
              self.$evalAsync(watchGroupAction);
13926
            }
13927
          });
13928
          deregisterFns.push(unwatchFn);
13929
        });
13930
 
13931
        function watchGroupAction() {
13932
          changeReactionScheduled = false;
13933
 
13934
          if (firstRun) {
13935
            firstRun = false;
13936
            listener(newValues, newValues, self);
13937
          } else {
13938
            listener(newValues, oldValues, self);
13939
          }
13940
        }
13941
 
13942
        return function deregisterWatchGroup() {
13943
          while (deregisterFns.length) {
13944
            deregisterFns.shift()();
13945
          }
13946
        };
13947
      },
13948
 
13949
 
13950
      /**
13951
       * @ngdoc method
13952
       * @name $rootScope.Scope#$watchCollection
13953
       * @kind function
13954
       *
13955
       * @description
13956
       * Shallow watches the properties of an object and fires whenever any of the properties change
13957
       * (for arrays, this implies watching the array items; for object maps, this implies watching
13958
       * the properties). If a change is detected, the `listener` callback is fired.
13959
       *
13960
       * - The `obj` collection is observed via standard $watch operation and is examined on every
13961
       *   call to $digest() to see if any items have been added, removed, or moved.
13962
       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
13963
       *   adding, removing, and moving items belonging to an object or array.
13964
       *
13965
       *
13966
       * # Example
13967
       * ```js
13968
          $scope.names = ['igor', 'matias', 'misko', 'james'];
13969
          $scope.dataCount = 4;
13970
 
13971
          $scope.$watchCollection('names', function(newNames, oldNames) {
13972
            $scope.dataCount = newNames.length;
13973
          });
13974
 
13975
          expect($scope.dataCount).toEqual(4);
13976
          $scope.$digest();
13977
 
13978
          //still at 4 ... no changes
13979
          expect($scope.dataCount).toEqual(4);
13980
 
13981
          $scope.names.pop();
13982
          $scope.$digest();
13983
 
13984
          //now there's been a change
13985
          expect($scope.dataCount).toEqual(3);
13986
       * ```
13987
       *
13988
       *
13989
       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
13990
       *    expression value should evaluate to an object or an array which is observed on each
13991
       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
13992
       *    collection will trigger a call to the `listener`.
13993
       *
13994
       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
13995
       *    when a change is detected.
13996
       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
13997
       *    - The `oldCollection` object is a copy of the former collection data.
13998
       *      Due to performance considerations, the`oldCollection` value is computed only if the
13999
       *      `listener` function declares two or more arguments.
14000
       *    - The `scope` argument refers to the current scope.
14001
       *
14002
       * @returns {function()} Returns a de-registration function for this listener. When the
14003
       *    de-registration function is executed, the internal watch operation is terminated.
14004
       */
14005
      $watchCollection: function(obj, listener) {
14006
        $watchCollectionInterceptor.$stateful = true;
14007
 
14008
        var self = this;
14009
        // the current value, updated on each dirty-check run
14010
        var newValue;
14011
        // a shallow copy of the newValue from the last dirty-check run,
14012
        // updated to match newValue during dirty-check run
14013
        var oldValue;
14014
        // a shallow copy of the newValue from when the last change happened
14015
        var veryOldValue;
14016
        // only track veryOldValue if the listener is asking for it
14017
        var trackVeryOldValue = (listener.length > 1);
14018
        var changeDetected = 0;
14019
        var changeDetector = $parse(obj, $watchCollectionInterceptor);
14020
        var internalArray = [];
14021
        var internalObject = {};
14022
        var initRun = true;
14023
        var oldLength = 0;
14024
 
14025
        function $watchCollectionInterceptor(_value) {
14026
          newValue = _value;
14027
          var newLength, key, bothNaN, newItem, oldItem;
14028
 
14029
          // If the new value is undefined, then return undefined as the watch may be a one-time watch
14030
          if (isUndefined(newValue)) return;
14031
 
14032
          if (!isObject(newValue)) { // if primitive
14033
            if (oldValue !== newValue) {
14034
              oldValue = newValue;
14035
              changeDetected++;
14036
            }
14037
          } else if (isArrayLike(newValue)) {
14038
            if (oldValue !== internalArray) {
14039
              // we are transitioning from something which was not an array into array.
14040
              oldValue = internalArray;
14041
              oldLength = oldValue.length = 0;
14042
              changeDetected++;
14043
            }
14044
 
14045
            newLength = newValue.length;
14046
 
14047
            if (oldLength !== newLength) {
14048
              // if lengths do not match we need to trigger change notification
14049
              changeDetected++;
14050
              oldValue.length = oldLength = newLength;
14051
            }
14052
            // copy the items to oldValue and look for changes.
14053
            for (var i = 0; i < newLength; i++) {
14054
              oldItem = oldValue[i];
14055
              newItem = newValue[i];
14056
 
14057
              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14058
              if (!bothNaN && (oldItem !== newItem)) {
14059
                changeDetected++;
14060
                oldValue[i] = newItem;
14061
              }
14062
            }
14063
          } else {
14064
            if (oldValue !== internalObject) {
14065
              // we are transitioning from something which was not an object into object.
14066
              oldValue = internalObject = {};
14067
              oldLength = 0;
14068
              changeDetected++;
14069
            }
14070
            // copy the items to oldValue and look for changes.
14071
            newLength = 0;
14072
            for (key in newValue) {
14073
              if (newValue.hasOwnProperty(key)) {
14074
                newLength++;
14075
                newItem = newValue[key];
14076
                oldItem = oldValue[key];
14077
 
14078
                if (key in oldValue) {
14079
                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14080
                  if (!bothNaN && (oldItem !== newItem)) {
14081
                    changeDetected++;
14082
                    oldValue[key] = newItem;
14083
                  }
14084
                } else {
14085
                  oldLength++;
14086
                  oldValue[key] = newItem;
14087
                  changeDetected++;
14088
                }
14089
              }
14090
            }
14091
            if (oldLength > newLength) {
14092
              // we used to have more keys, need to find them and destroy them.
14093
              changeDetected++;
14094
              for (key in oldValue) {
14095
                if (!newValue.hasOwnProperty(key)) {
14096
                  oldLength--;
14097
                  delete oldValue[key];
14098
                }
14099
              }
14100
            }
14101
          }
14102
          return changeDetected;
14103
        }
14104
 
14105
        function $watchCollectionAction() {
14106
          if (initRun) {
14107
            initRun = false;
14108
            listener(newValue, newValue, self);
14109
          } else {
14110
            listener(newValue, veryOldValue, self);
14111
          }
14112
 
14113
          // make a copy for the next time a collection is changed
14114
          if (trackVeryOldValue) {
14115
            if (!isObject(newValue)) {
14116
              //primitive
14117
              veryOldValue = newValue;
14118
            } else if (isArrayLike(newValue)) {
14119
              veryOldValue = new Array(newValue.length);
14120
              for (var i = 0; i < newValue.length; i++) {
14121
                veryOldValue[i] = newValue[i];
14122
              }
14123
            } else { // if object
14124
              veryOldValue = {};
14125
              for (var key in newValue) {
14126
                if (hasOwnProperty.call(newValue, key)) {
14127
                  veryOldValue[key] = newValue[key];
14128
                }
14129
              }
14130
            }
14131
          }
14132
        }
14133
 
14134
        return this.$watch(changeDetector, $watchCollectionAction);
14135
      },
14136
 
14137
      /**
14138
       * @ngdoc method
14139
       * @name $rootScope.Scope#$digest
14140
       * @kind function
14141
       *
14142
       * @description
14143
       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
14144
       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
14145
       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
14146
       * until no more listeners are firing. This means that it is possible to get into an infinite
14147
       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
14148
       * iterations exceeds 10.
14149
       *
14150
       * Usually, you don't call `$digest()` directly in
14151
       * {@link ng.directive:ngController controllers} or in
14152
       * {@link ng.$compileProvider#directive directives}.
14153
       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
14154
       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
14155
       *
14156
       * If you want to be notified whenever `$digest()` is called,
14157
       * you can register a `watchExpression` function with
14158
       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
14159
       *
14160
       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
14161
       *
14162
       * # Example
14163
       * ```js
14164
           var scope = ...;
14165
           scope.name = 'misko';
14166
           scope.counter = 0;
14167
 
14168
           expect(scope.counter).toEqual(0);
14169
           scope.$watch('name', function(newValue, oldValue) {
14170
             scope.counter = scope.counter + 1;
14171
           });
14172
           expect(scope.counter).toEqual(0);
14173
 
14174
           scope.$digest();
14175
           // the listener is always called during the first $digest loop after it was registered
14176
           expect(scope.counter).toEqual(1);
14177
 
14178
           scope.$digest();
14179
           // but now it will not be called unless the value changes
14180
           expect(scope.counter).toEqual(1);
14181
 
14182
           scope.name = 'adam';
14183
           scope.$digest();
14184
           expect(scope.counter).toEqual(2);
14185
       * ```
14186
       *
14187
       */
14188
      $digest: function() {
14189
        var watch, value, last,
14190
            watchers,
14191
            length,
14192
            dirty, ttl = TTL,
14193
            next, current, target = this,
14194
            watchLog = [],
14195
            logIdx, logMsg, asyncTask;
14196
 
14197
        beginPhase('$digest');
14198
        // Check for changes to browser url that happened in sync before the call to $digest
14199
        $browser.$$checkUrlChange();
14200
 
14201
        if (this === $rootScope && applyAsyncId !== null) {
14202
          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
14203
          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
14204
          $browser.defer.cancel(applyAsyncId);
14205
          flushApplyAsync();
14206
        }
14207
 
14208
        lastDirtyWatch = null;
14209
 
14210
        do { // "while dirty" loop
14211
          dirty = false;
14212
          current = target;
14213
 
14214
          while (asyncQueue.length) {
14215
            try {
14216
              asyncTask = asyncQueue.shift();
14217
              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
14218
            } catch (e) {
14219
              $exceptionHandler(e);
14220
            }
14221
            lastDirtyWatch = null;
14222
          }
14223
 
14224
          traverseScopesLoop:
14225
          do { // "traverse the scopes" loop
14226
            if ((watchers = current.$$watchers)) {
14227
              // process our watches
14228
              length = watchers.length;
14229
              while (length--) {
14230
                try {
14231
                  watch = watchers[length];
14232
                  // Most common watches are on primitives, in which case we can short
14233
                  // circuit it with === operator, only when === fails do we use .equals
14234
                  if (watch) {
14235
                    if ((value = watch.get(current)) !== (last = watch.last) &&
14236
                        !(watch.eq
14237
                            ? equals(value, last)
14238
                            : (typeof value === 'number' && typeof last === 'number'
14239
                               && isNaN(value) && isNaN(last)))) {
14240
                      dirty = true;
14241
                      lastDirtyWatch = watch;
14242
                      watch.last = watch.eq ? copy(value, null) : value;
14243
                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
14244
                      if (ttl < 5) {
14245
                        logIdx = 4 - ttl;
14246
                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
14247
                        watchLog[logIdx].push({
14248
                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14249
                          newVal: value,
14250
                          oldVal: last
14251
                        });
14252
                      }
14253
                    } else if (watch === lastDirtyWatch) {
14254
                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
14255
                      // have already been tested.
14256
                      dirty = false;
14257
                      break traverseScopesLoop;
14258
                    }
14259
                  }
14260
                } catch (e) {
14261
                  $exceptionHandler(e);
14262
                }
14263
              }
14264
            }
14265
 
14266
            // Insanity Warning: scope depth-first traversal
14267
            // yes, this code is a bit crazy, but it works and we have tests to prove it!
14268
            // this piece should be kept in sync with the traversal in $broadcast
14269
            if (!(next = (current.$$childHead ||
14270
                (current !== target && current.$$nextSibling)))) {
14271
              while (current !== target && !(next = current.$$nextSibling)) {
14272
                current = current.$parent;
14273
              }
14274
            }
14275
          } while ((current = next));
14276
 
14277
          // `break traverseScopesLoop;` takes us to here
14278
 
14279
          if ((dirty || asyncQueue.length) && !(ttl--)) {
14280
            clearPhase();
14281
            throw $rootScopeMinErr('infdig',
14282
                '{0} $digest() iterations reached. Aborting!\n' +
14283
                'Watchers fired in the last 5 iterations: {1}',
14284
                TTL, watchLog);
14285
          }
14286
 
14287
        } while (dirty || asyncQueue.length);
14288
 
14289
        clearPhase();
14290
 
14291
        while (postDigestQueue.length) {
14292
          try {
14293
            postDigestQueue.shift()();
14294
          } catch (e) {
14295
            $exceptionHandler(e);
14296
          }
14297
        }
14298
      },
14299
 
14300
 
14301
      /**
14302
       * @ngdoc event
14303
       * @name $rootScope.Scope#$destroy
14304
       * @eventType broadcast on scope being destroyed
14305
       *
14306
       * @description
14307
       * Broadcasted when a scope and its children are being destroyed.
14308
       *
14309
       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14310
       * clean up DOM bindings before an element is removed from the DOM.
14311
       */
14312
 
14313
      /**
14314
       * @ngdoc method
14315
       * @name $rootScope.Scope#$destroy
14316
       * @kind function
14317
       *
14318
       * @description
14319
       * Removes the current scope (and all of its children) from the parent scope. Removal implies
14320
       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
14321
       * propagate to the current scope and its children. Removal also implies that the current
14322
       * scope is eligible for garbage collection.
14323
       *
14324
       * The `$destroy()` is usually used by directives such as
14325
       * {@link ng.directive:ngRepeat ngRepeat} for managing the
14326
       * unrolling of the loop.
14327
       *
14328
       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
14329
       * Application code can register a `$destroy` event handler that will give it a chance to
14330
       * perform any necessary cleanup.
14331
       *
14332
       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14333
       * clean up DOM bindings before an element is removed from the DOM.
14334
       */
14335
      $destroy: function() {
14336
        // we can't destroy the root scope or a scope that has been already destroyed
14337
        if (this.$$destroyed) return;
14338
        var parent = this.$parent;
14339
 
14340
        this.$broadcast('$destroy');
14341
        this.$$destroyed = true;
14342
        if (this === $rootScope) return;
14343
 
14344
        for (var eventName in this.$$listenerCount) {
14345
          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
14346
        }
14347
 
14348
        // sever all the references to parent scopes (after this cleanup, the current scope should
14349
        // not be retained by any of our references and should be eligible for garbage collection)
14350
        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
14351
        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
14352
        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
14353
        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
14354
 
14355
        // Disable listeners, watchers and apply/digest methods
14356
        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
14357
        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
14358
        this.$$listeners = {};
14359
 
14360
        // All of the code below is bogus code that works around V8's memory leak via optimized code
14361
        // and inline caches.
14362
        //
14363
        // see:
14364
        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
14365
        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
14366
        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
14367
 
14368
        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
14369
            this.$$childTail = this.$root = this.$$watchers = null;
14370
      },
14371
 
14372
      /**
14373
       * @ngdoc method
14374
       * @name $rootScope.Scope#$eval
14375
       * @kind function
14376
       *
14377
       * @description
14378
       * Executes the `expression` on the current scope and returns the result. Any exceptions in
14379
       * the expression are propagated (uncaught). This is useful when evaluating Angular
14380
       * expressions.
14381
       *
14382
       * # Example
14383
       * ```js
14384
           var scope = ng.$rootScope.Scope();
14385
           scope.a = 1;
14386
           scope.b = 2;
14387
 
14388
           expect(scope.$eval('a+b')).toEqual(3);
14389
           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
14390
       * ```
14391
       *
14392
       * @param {(string|function())=} expression An angular expression to be executed.
14393
       *
14394
       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
14395
       *    - `function(scope)`: execute the function with the current `scope` parameter.
14396
       *
14397
       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14398
       * @returns {*} The result of evaluating the expression.
14399
       */
14400
      $eval: function(expr, locals) {
14401
        return $parse(expr)(this, locals);
14402
      },
14403
 
14404
      /**
14405
       * @ngdoc method
14406
       * @name $rootScope.Scope#$evalAsync
14407
       * @kind function
14408
       *
14409
       * @description
14410
       * Executes the expression on the current scope at a later point in time.
14411
       *
14412
       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
14413
       * that:
14414
       *
14415
       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
14416
       *     rendering).
14417
       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
14418
       *     `expression` execution.
14419
       *
14420
       * Any exceptions from the execution of the expression are forwarded to the
14421
       * {@link ng.$exceptionHandler $exceptionHandler} service.
14422
       *
14423
       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
14424
       * will be scheduled. However, it is encouraged to always call code that changes the model
14425
       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
14426
       *
14427
       * @param {(string|function())=} expression An angular expression to be executed.
14428
       *
14429
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14430
       *    - `function(scope)`: execute the function with the current `scope` parameter.
14431
       *
14432
       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14433
       */
14434
      $evalAsync: function(expr, locals) {
14435
        // if we are outside of an $digest loop and this is the first time we are scheduling async
14436
        // task also schedule async auto-flush
14437
        if (!$rootScope.$$phase && !asyncQueue.length) {
14438
          $browser.defer(function() {
14439
            if (asyncQueue.length) {
14440
              $rootScope.$digest();
14441
            }
14442
          });
14443
        }
14444
 
14445
        asyncQueue.push({scope: this, expression: expr, locals: locals});
14446
      },
14447
 
14448
      $$postDigest: function(fn) {
14449
        postDigestQueue.push(fn);
14450
      },
14451
 
14452
      /**
14453
       * @ngdoc method
14454
       * @name $rootScope.Scope#$apply
14455
       * @kind function
14456
       *
14457
       * @description
14458
       * `$apply()` is used to execute an expression in angular from outside of the angular
14459
       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
14460
       * Because we are calling into the angular framework we need to perform proper scope life
14461
       * cycle of {@link ng.$exceptionHandler exception handling},
14462
       * {@link ng.$rootScope.Scope#$digest executing watches}.
14463
       *
14464
       * ## Life cycle
14465
       *
14466
       * # Pseudo-Code of `$apply()`
14467
       * ```js
14468
           function $apply(expr) {
14469
             try {
14470
               return $eval(expr);
14471
             } catch (e) {
14472
               $exceptionHandler(e);
14473
             } finally {
14474
               $root.$digest();
14475
             }
14476
           }
14477
       * ```
14478
       *
14479
       *
14480
       * Scope's `$apply()` method transitions through the following stages:
14481
       *
14482
       * 1. The {@link guide/expression expression} is executed using the
14483
       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
14484
       * 2. Any exceptions from the execution of the expression are forwarded to the
14485
       *    {@link ng.$exceptionHandler $exceptionHandler} service.
14486
       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
14487
       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
14488
       *
14489
       *
14490
       * @param {(string|function())=} exp An angular expression to be executed.
14491
       *
14492
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14493
       *    - `function(scope)`: execute the function with current `scope` parameter.
14494
       *
14495
       * @returns {*} The result of evaluating the expression.
14496
       */
14497
      $apply: function(expr) {
14498
        try {
14499
          beginPhase('$apply');
14500
          return this.$eval(expr);
14501
        } catch (e) {
14502
          $exceptionHandler(e);
14503
        } finally {
14504
          clearPhase();
14505
          try {
14506
            $rootScope.$digest();
14507
          } catch (e) {
14508
            $exceptionHandler(e);
14509
            throw e;
14510
          }
14511
        }
14512
      },
14513
 
14514
      /**
14515
       * @ngdoc method
14516
       * @name $rootScope.Scope#$applyAsync
14517
       * @kind function
14518
       *
14519
       * @description
14520
       * Schedule the invocation of $apply to occur at a later time. The actual time difference
14521
       * varies across browsers, but is typically around ~10 milliseconds.
14522
       *
14523
       * This can be used to queue up multiple expressions which need to be evaluated in the same
14524
       * digest.
14525
       *
14526
       * @param {(string|function())=} exp An angular expression to be executed.
14527
       *
14528
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14529
       *    - `function(scope)`: execute the function with current `scope` parameter.
14530
       */
14531
      $applyAsync: function(expr) {
14532
        var scope = this;
14533
        expr && applyAsyncQueue.push($applyAsyncExpression);
14534
        scheduleApplyAsync();
14535
 
14536
        function $applyAsyncExpression() {
14537
          scope.$eval(expr);
14538
        }
14539
      },
14540
 
14541
      /**
14542
       * @ngdoc method
14543
       * @name $rootScope.Scope#$on
14544
       * @kind function
14545
       *
14546
       * @description
14547
       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
14548
       * discussion of event life cycle.
14549
       *
14550
       * The event listener function format is: `function(event, args...)`. The `event` object
14551
       * passed into the listener has the following attributes:
14552
       *
14553
       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
14554
       *     `$broadcast`-ed.
14555
       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
14556
       *     event propagates through the scope hierarchy, this property is set to null.
14557
       *   - `name` - `{string}`: name of the event.
14558
       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
14559
       *     further event propagation (available only for events that were `$emit`-ed).
14560
       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
14561
       *     to true.
14562
       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
14563
       *
14564
       * @param {string} name Event name to listen on.
14565
       * @param {function(event, ...args)} listener Function to call when the event is emitted.
14566
       * @returns {function()} Returns a deregistration function for this listener.
14567
       */
14568
      $on: function(name, listener) {
14569
        var namedListeners = this.$$listeners[name];
14570
        if (!namedListeners) {
14571
          this.$$listeners[name] = namedListeners = [];
14572
        }
14573
        namedListeners.push(listener);
14574
 
14575
        var current = this;
14576
        do {
14577
          if (!current.$$listenerCount[name]) {
14578
            current.$$listenerCount[name] = 0;
14579
          }
14580
          current.$$listenerCount[name]++;
14581
        } while ((current = current.$parent));
14582
 
14583
        var self = this;
14584
        return function() {
14585
          var indexOfListener = namedListeners.indexOf(listener);
14586
          if (indexOfListener !== -1) {
14587
            namedListeners[indexOfListener] = null;
14588
            decrementListenerCount(self, 1, name);
14589
          }
14590
        };
14591
      },
14592
 
14593
 
14594
      /**
14595
       * @ngdoc method
14596
       * @name $rootScope.Scope#$emit
14597
       * @kind function
14598
       *
14599
       * @description
14600
       * Dispatches an event `name` upwards through the scope hierarchy notifying the
14601
       * registered {@link ng.$rootScope.Scope#$on} listeners.
14602
       *
14603
       * The event life cycle starts at the scope on which `$emit` was called. All
14604
       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14605
       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
14606
       * registered listeners along the way. The event will stop propagating if one of the listeners
14607
       * cancels it.
14608
       *
14609
       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14610
       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14611
       *
14612
       * @param {string} name Event name to emit.
14613
       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14614
       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
14615
       */
14616
      $emit: function(name, args) {
14617
        var empty = [],
14618
            namedListeners,
14619
            scope = this,
14620
            stopPropagation = false,
14621
            event = {
14622
              name: name,
14623
              targetScope: scope,
14624
              stopPropagation: function() {stopPropagation = true;},
14625
              preventDefault: function() {
14626
                event.defaultPrevented = true;
14627
              },
14628
              defaultPrevented: false
14629
            },
14630
            listenerArgs = concat([event], arguments, 1),
14631
            i, length;
14632
 
14633
        do {
14634
          namedListeners = scope.$$listeners[name] || empty;
14635
          event.currentScope = scope;
14636
          for (i = 0, length = namedListeners.length; i < length; i++) {
14637
 
14638
            // if listeners were deregistered, defragment the array
14639
            if (!namedListeners[i]) {
14640
              namedListeners.splice(i, 1);
14641
              i--;
14642
              length--;
14643
              continue;
14644
            }
14645
            try {
14646
              //allow all listeners attached to the current scope to run
14647
              namedListeners[i].apply(null, listenerArgs);
14648
            } catch (e) {
14649
              $exceptionHandler(e);
14650
            }
14651
          }
14652
          //if any listener on the current scope stops propagation, prevent bubbling
14653
          if (stopPropagation) {
14654
            event.currentScope = null;
14655
            return event;
14656
          }
14657
          //traverse upwards
14658
          scope = scope.$parent;
14659
        } while (scope);
14660
 
14661
        event.currentScope = null;
14662
 
14663
        return event;
14664
      },
14665
 
14666
 
14667
      /**
14668
       * @ngdoc method
14669
       * @name $rootScope.Scope#$broadcast
14670
       * @kind function
14671
       *
14672
       * @description
14673
       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
14674
       * registered {@link ng.$rootScope.Scope#$on} listeners.
14675
       *
14676
       * The event life cycle starts at the scope on which `$broadcast` was called. All
14677
       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14678
       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
14679
       * scope and calls all registered listeners along the way. The event cannot be canceled.
14680
       *
14681
       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14682
       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14683
       *
14684
       * @param {string} name Event name to broadcast.
14685
       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14686
       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
14687
       */
14688
      $broadcast: function(name, args) {
14689
        var target = this,
14690
            current = target,
14691
            next = target,
14692
            event = {
14693
              name: name,
14694
              targetScope: target,
14695
              preventDefault: function() {
14696
                event.defaultPrevented = true;
14697
              },
14698
              defaultPrevented: false
14699
            };
14700
 
14701
        if (!target.$$listenerCount[name]) return event;
14702
 
14703
        var listenerArgs = concat([event], arguments, 1),
14704
            listeners, i, length;
14705
 
14706
        //down while you can, then up and next sibling or up and next sibling until back at root
14707
        while ((current = next)) {
14708
          event.currentScope = current;
14709
          listeners = current.$$listeners[name] || [];
14710
          for (i = 0, length = listeners.length; i < length; i++) {
14711
            // if listeners were deregistered, defragment the array
14712
            if (!listeners[i]) {
14713
              listeners.splice(i, 1);
14714
              i--;
14715
              length--;
14716
              continue;
14717
            }
14718
 
14719
            try {
14720
              listeners[i].apply(null, listenerArgs);
14721
            } catch (e) {
14722
              $exceptionHandler(e);
14723
            }
14724
          }
14725
 
14726
          // Insanity Warning: scope depth-first traversal
14727
          // yes, this code is a bit crazy, but it works and we have tests to prove it!
14728
          // this piece should be kept in sync with the traversal in $digest
14729
          // (though it differs due to having the extra check for $$listenerCount)
14730
          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
14731
              (current !== target && current.$$nextSibling)))) {
14732
            while (current !== target && !(next = current.$$nextSibling)) {
14733
              current = current.$parent;
14734
            }
14735
          }
14736
        }
14737
 
14738
        event.currentScope = null;
14739
        return event;
14740
      }
14741
    };
14742
 
14743
    var $rootScope = new Scope();
14744
 
14745
    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
14746
    var asyncQueue = $rootScope.$$asyncQueue = [];
14747
    var postDigestQueue = $rootScope.$$postDigestQueue = [];
14748
    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
14749
 
14750
    return $rootScope;
14751
 
14752
 
14753
    function beginPhase(phase) {
14754
      if ($rootScope.$$phase) {
14755
        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
14756
      }
14757
 
14758
      $rootScope.$$phase = phase;
14759
    }
14760
 
14761
    function clearPhase() {
14762
      $rootScope.$$phase = null;
14763
    }
14764
 
14765
 
14766
    function decrementListenerCount(current, count, name) {
14767
      do {
14768
        current.$$listenerCount[name] -= count;
14769
 
14770
        if (current.$$listenerCount[name] === 0) {
14771
          delete current.$$listenerCount[name];
14772
        }
14773
      } while ((current = current.$parent));
14774
    }
14775
 
14776
    /**
14777
     * function used as an initial value for watchers.
14778
     * because it's unique we can easily tell it apart from other values
14779
     */
14780
    function initWatchVal() {}
14781
 
14782
    function flushApplyAsync() {
14783
      while (applyAsyncQueue.length) {
14784
        try {
14785
          applyAsyncQueue.shift()();
14786
        } catch (e) {
14787
          $exceptionHandler(e);
14788
        }
14789
      }
14790
      applyAsyncId = null;
14791
    }
14792
 
14793
    function scheduleApplyAsync() {
14794
      if (applyAsyncId === null) {
14795
        applyAsyncId = $browser.defer(function() {
14796
          $rootScope.$apply(flushApplyAsync);
14797
        });
14798
      }
14799
    }
14800
  }];
14801
}
14802
 
14803
/**
14804
 * @description
14805
 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
14806
 */
14807
function $$SanitizeUriProvider() {
14808
  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14809
    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
14810
 
14811
  /**
14812
   * @description
14813
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14814
   * urls during a[href] sanitization.
14815
   *
14816
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14817
   *
14818
   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
14819
   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
14820
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14821
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14822
   *
14823
   * @param {RegExp=} regexp New regexp to whitelist urls with.
14824
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14825
   *    chaining otherwise.
14826
   */
14827
  this.aHrefSanitizationWhitelist = function(regexp) {
14828
    if (isDefined(regexp)) {
14829
      aHrefSanitizationWhitelist = regexp;
14830
      return this;
14831
    }
14832
    return aHrefSanitizationWhitelist;
14833
  };
14834
 
14835
 
14836
  /**
14837
   * @description
14838
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14839
   * urls during img[src] sanitization.
14840
   *
14841
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14842
   *
14843
   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
14844
   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
14845
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14846
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14847
   *
14848
   * @param {RegExp=} regexp New regexp to whitelist urls with.
14849
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14850
   *    chaining otherwise.
14851
   */
14852
  this.imgSrcSanitizationWhitelist = function(regexp) {
14853
    if (isDefined(regexp)) {
14854
      imgSrcSanitizationWhitelist = regexp;
14855
      return this;
14856
    }
14857
    return imgSrcSanitizationWhitelist;
14858
  };
14859
 
14860
  this.$get = function() {
14861
    return function sanitizeUri(uri, isImage) {
14862
      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
14863
      var normalizedVal;
14864
      normalizedVal = urlResolve(uri).href;
14865
      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
14866
        return 'unsafe:' + normalizedVal;
14867
      }
14868
      return uri;
14869
    };
14870
  };
14871
}
14872
 
14873
var $sceMinErr = minErr('$sce');
14874
 
14875
var SCE_CONTEXTS = {
14876
  HTML: 'html',
14877
  CSS: 'css',
14878
  URL: 'url',
14879
  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
14880
  // url.  (e.g. ng-include, script src, templateUrl)
14881
  RESOURCE_URL: 'resourceUrl',
14882
  JS: 'js'
14883
};
14884
 
14885
// Helper functions follow.
14886
 
14887
function adjustMatcher(matcher) {
14888
  if (matcher === 'self') {
14889
    return matcher;
14890
  } else if (isString(matcher)) {
14891
    // Strings match exactly except for 2 wildcards - '*' and '**'.
14892
    // '*' matches any character except those from the set ':/.?&'.
14893
    // '**' matches any character (like .* in a RegExp).
14894
    // More than 2 *'s raises an error as it's ill defined.
14895
    if (matcher.indexOf('***') > -1) {
14896
      throw $sceMinErr('iwcard',
14897
          'Illegal sequence *** in string matcher.  String: {0}', matcher);
14898
    }
14899
    matcher = escapeForRegexp(matcher).
14900
                  replace('\\*\\*', '.*').
14901
                  replace('\\*', '[^:/.?&;]*');
14902
    return new RegExp('^' + matcher + '$');
14903
  } else if (isRegExp(matcher)) {
14904
    // The only other type of matcher allowed is a Regexp.
14905
    // Match entire URL / disallow partial matches.
14906
    // Flags are reset (i.e. no global, ignoreCase or multiline)
14907
    return new RegExp('^' + matcher.source + '$');
14908
  } else {
14909
    throw $sceMinErr('imatcher',
14910
        'Matchers may only be "self", string patterns or RegExp objects');
14911
  }
14912
}
14913
 
14914
 
14915
function adjustMatchers(matchers) {
14916
  var adjustedMatchers = [];
14917
  if (isDefined(matchers)) {
14918
    forEach(matchers, function(matcher) {
14919
      adjustedMatchers.push(adjustMatcher(matcher));
14920
    });
14921
  }
14922
  return adjustedMatchers;
14923
}
14924
 
14925
 
14926
/**
14927
 * @ngdoc service
14928
 * @name $sceDelegate
14929
 * @kind function
14930
 *
14931
 * @description
14932
 *
14933
 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
14934
 * Contextual Escaping (SCE)} services to AngularJS.
14935
 *
14936
 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
14937
 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
14938
 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
14939
 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
14940
 * work because `$sce` delegates to `$sceDelegate` for these operations.
14941
 *
14942
 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
14943
 *
14944
 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
14945
 * can override it completely to change the behavior of `$sce`, the common case would
14946
 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
14947
 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
14948
 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
14949
 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
14950
 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14951
 */
14952
 
14953
/**
14954
 * @ngdoc provider
14955
 * @name $sceDelegateProvider
14956
 * @description
14957
 *
14958
 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
14959
 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
14960
 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
14961
 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
14962
 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14963
 *
14964
 * For the general details about this service in Angular, read the main page for {@link ng.$sce
14965
 * Strict Contextual Escaping (SCE)}.
14966
 *
14967
 * **Example**:  Consider the following case. <a name="example"></a>
14968
 *
14969
 * - your app is hosted at url `http://myapp.example.com/`
14970
 * - but some of your templates are hosted on other domains you control such as
14971
 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
14972
 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
14973
 *
14974
 * Here is what a secure configuration for this scenario might look like:
14975
 *
14976
 * ```
14977
 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
14978
 *    $sceDelegateProvider.resourceUrlWhitelist([
14979
 *      // Allow same origin resource loads.
14980
 *      'self',
14981
 *      // Allow loading from our assets domain.  Notice the difference between * and **.
14982
 *      'http://srv*.assets.example.com/**'
14983
 *    ]);
14984
 *
14985
 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
14986
 *    $sceDelegateProvider.resourceUrlBlacklist([
14987
 *      'http://myapp.example.com/clickThru**'
14988
 *    ]);
14989
 *  });
14990
 * ```
14991
 */
14992
 
14993
function $SceDelegateProvider() {
14994
  this.SCE_CONTEXTS = SCE_CONTEXTS;
14995
 
14996
  // Resource URLs can also be trusted by policy.
14997
  var resourceUrlWhitelist = ['self'],
14998
      resourceUrlBlacklist = [];
14999
 
15000
  /**
15001
   * @ngdoc method
15002
   * @name $sceDelegateProvider#resourceUrlWhitelist
15003
   * @kind function
15004
   *
15005
   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
15006
   *     provided.  This must be an array or null.  A snapshot of this array is used so further
15007
   *     changes to the array are ignored.
15008
   *
15009
   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15010
   *     allowed in this array.
15011
   *
15012
   *     Note: **an empty whitelist array will block all URLs**!
15013
   *
15014
   * @return {Array} the currently set whitelist array.
15015
   *
15016
   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
15017
   * same origin resource requests.
15018
   *
15019
   * @description
15020
   * Sets/Gets the whitelist of trusted resource URLs.
15021
   */
15022
  this.resourceUrlWhitelist = function(value) {
15023
    if (arguments.length) {
15024
      resourceUrlWhitelist = adjustMatchers(value);
15025
    }
15026
    return resourceUrlWhitelist;
15027
  };
15028
 
15029
  /**
15030
   * @ngdoc method
15031
   * @name $sceDelegateProvider#resourceUrlBlacklist
15032
   * @kind function
15033
   *
15034
   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
15035
   *     provided.  This must be an array or null.  A snapshot of this array is used so further
15036
   *     changes to the array are ignored.
15037
   *
15038
   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15039
   *     allowed in this array.
15040
   *
15041
   *     The typical usage for the blacklist is to **block
15042
   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
15043
   *     these would otherwise be trusted but actually return content from the redirected domain.
15044
   *
15045
   *     Finally, **the blacklist overrides the whitelist** and has the final say.
15046
   *
15047
   * @return {Array} the currently set blacklist array.
15048
   *
15049
   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
15050
   * is no blacklist.)
15051
   *
15052
   * @description
15053
   * Sets/Gets the blacklist of trusted resource URLs.
15054
   */
15055
 
15056
  this.resourceUrlBlacklist = function(value) {
15057
    if (arguments.length) {
15058
      resourceUrlBlacklist = adjustMatchers(value);
15059
    }
15060
    return resourceUrlBlacklist;
15061
  };
15062
 
15063
  this.$get = ['$injector', function($injector) {
15064
 
15065
    var htmlSanitizer = function htmlSanitizer(html) {
15066
      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15067
    };
15068
 
15069
    if ($injector.has('$sanitize')) {
15070
      htmlSanitizer = $injector.get('$sanitize');
15071
    }
15072
 
15073
 
15074
    function matchUrl(matcher, parsedUrl) {
15075
      if (matcher === 'self') {
15076
        return urlIsSameOrigin(parsedUrl);
15077
      } else {
15078
        // definitely a regex.  See adjustMatchers()
15079
        return !!matcher.exec(parsedUrl.href);
15080
      }
15081
    }
15082
 
15083
    function isResourceUrlAllowedByPolicy(url) {
15084
      var parsedUrl = urlResolve(url.toString());
15085
      var i, n, allowed = false;
15086
      // Ensure that at least one item from the whitelist allows this url.
15087
      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
15088
        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
15089
          allowed = true;
15090
          break;
15091
        }
15092
      }
15093
      if (allowed) {
15094
        // Ensure that no item from the blacklist blocked this url.
15095
        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
15096
          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
15097
            allowed = false;
15098
            break;
15099
          }
15100
        }
15101
      }
15102
      return allowed;
15103
    }
15104
 
15105
    function generateHolderType(Base) {
15106
      var holderType = function TrustedValueHolderType(trustedValue) {
15107
        this.$$unwrapTrustedValue = function() {
15108
          return trustedValue;
15109
        };
15110
      };
15111
      if (Base) {
15112
        holderType.prototype = new Base();
15113
      }
15114
      holderType.prototype.valueOf = function sceValueOf() {
15115
        return this.$$unwrapTrustedValue();
15116
      };
15117
      holderType.prototype.toString = function sceToString() {
15118
        return this.$$unwrapTrustedValue().toString();
15119
      };
15120
      return holderType;
15121
    }
15122
 
15123
    var trustedValueHolderBase = generateHolderType(),
15124
        byType = {};
15125
 
15126
    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
15127
    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
15128
    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
15129
    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
15130
    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
15131
 
15132
    /**
15133
     * @ngdoc method
15134
     * @name $sceDelegate#trustAs
15135
     *
15136
     * @description
15137
     * Returns an object that is trusted by angular for use in specified strict
15138
     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
15139
     * attribute interpolation, any dom event binding attribute interpolation
15140
     * such as for onclick,  etc.) that uses the provided value.
15141
     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
15142
     *
15143
     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
15144
     *   resourceUrl, html, js and css.
15145
     * @param {*} value The value that that should be considered trusted/safe.
15146
     * @returns {*} A value that can be used to stand in for the provided `value` in places
15147
     * where Angular expects a $sce.trustAs() return value.
15148
     */
15149
    function trustAs(type, trustedValue) {
15150
      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15151
      if (!Constructor) {
15152
        throw $sceMinErr('icontext',
15153
            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
15154
            type, trustedValue);
15155
      }
15156
      if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
15157
        return trustedValue;
15158
      }
15159
      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
15160
      // mutable objects, we ensure here that the value passed in is actually a string.
15161
      if (typeof trustedValue !== 'string') {
15162
        throw $sceMinErr('itype',
15163
            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
15164
            type);
15165
      }
15166
      return new Constructor(trustedValue);
15167
    }
15168
 
15169
    /**
15170
     * @ngdoc method
15171
     * @name $sceDelegate#valueOf
15172
     *
15173
     * @description
15174
     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
15175
     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
15176
     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
15177
     *
15178
     * If the passed parameter is not a value that had been returned by {@link
15179
     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
15180
     *
15181
     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
15182
     *      call or anything else.
15183
     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
15184
     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
15185
     *     `value` unchanged.
15186
     */
15187
    function valueOf(maybeTrusted) {
15188
      if (maybeTrusted instanceof trustedValueHolderBase) {
15189
        return maybeTrusted.$$unwrapTrustedValue();
15190
      } else {
15191
        return maybeTrusted;
15192
      }
15193
    }
15194
 
15195
    /**
15196
     * @ngdoc method
15197
     * @name $sceDelegate#getTrusted
15198
     *
15199
     * @description
15200
     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
15201
     * returns the originally supplied value if the queried context type is a supertype of the
15202
     * created type.  If this condition isn't satisfied, throws an exception.
15203
     *
15204
     * @param {string} type The kind of context in which this value is to be used.
15205
     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
15206
     *     `$sceDelegate.trustAs`} call.
15207
     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
15208
     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
15209
     */
15210
    function getTrusted(type, maybeTrusted) {
15211
      if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
15212
        return maybeTrusted;
15213
      }
15214
      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15215
      if (constructor && maybeTrusted instanceof constructor) {
15216
        return maybeTrusted.$$unwrapTrustedValue();
15217
      }
15218
      // If we get here, then we may only take one of two actions.
15219
      // 1. sanitize the value for the requested type, or
15220
      // 2. throw an exception.
15221
      if (type === SCE_CONTEXTS.RESOURCE_URL) {
15222
        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
15223
          return maybeTrusted;
15224
        } else {
15225
          throw $sceMinErr('insecurl',
15226
              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
15227
              maybeTrusted.toString());
15228
        }
15229
      } else if (type === SCE_CONTEXTS.HTML) {
15230
        return htmlSanitizer(maybeTrusted);
15231
      }
15232
      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15233
    }
15234
 
15235
    return { trustAs: trustAs,
15236
             getTrusted: getTrusted,
15237
             valueOf: valueOf };
15238
  }];
15239
}
15240
 
15241
 
15242
/**
15243
 * @ngdoc provider
15244
 * @name $sceProvider
15245
 * @description
15246
 *
15247
 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
15248
 * -   enable/disable Strict Contextual Escaping (SCE) in a module
15249
 * -   override the default implementation with a custom delegate
15250
 *
15251
 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
15252
 */
15253
 
15254
/* jshint maxlen: false*/
15255
 
15256
/**
15257
 * @ngdoc service
15258
 * @name $sce
15259
 * @kind function
15260
 *
15261
 * @description
15262
 *
15263
 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
15264
 *
15265
 * # Strict Contextual Escaping
15266
 *
15267
 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
15268
 * contexts to result in a value that is marked as safe to use for that context.  One example of
15269
 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
15270
 * to these contexts as privileged or SCE contexts.
15271
 *
15272
 * As of version 1.2, Angular ships with SCE enabled by default.
15273
 *
15274
 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
15275
 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
15276
 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
15277
 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
15278
 * to the top of your HTML document.
15279
 *
15280
 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
15281
 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
15282
 *
15283
 * Here's an example of a binding in a privileged context:
15284
 *
15285
 * ```
15286
 * <input ng-model="userHtml">
15287
 * <div ng-bind-html="userHtml"></div>
15288
 * ```
15289
 *
15290
 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
15291
 * disabled, this application allows the user to render arbitrary HTML into the DIV.
15292
 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
15293
 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
15294
 * security vulnerabilities.)
15295
 *
15296
 * For the case of HTML, you might use a library, either on the client side, or on the server side,
15297
 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
15298
 *
15299
 * How would you ensure that every place that used these types of bindings was bound to a value that
15300
 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
15301
 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
15302
 * properties/fields and forgot to update the binding to the sanitized value?
15303
 *
15304
 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
15305
 * determine that something explicitly says it's safe to use a value for binding in that
15306
 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
15307
 * for those values that you can easily tell are safe - because they were received from your server,
15308
 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
15309
 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
15310
 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
15311
 *
15312
 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
15313
 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
15314
 * obtain values that will be accepted by SCE / privileged contexts.
15315
 *
15316
 *
15317
 * ## How does it work?
15318
 *
15319
 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
15320
 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
15321
 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
15322
 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
15323
 *
15324
 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
15325
 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
15326
 * simplified):
15327
 *
15328
 * ```
15329
 * var ngBindHtmlDirective = ['$sce', function($sce) {
15330
 *   return function(scope, element, attr) {
15331
 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
15332
 *       element.html(value || '');
15333
 *     });
15334
 *   };
15335
 * }];
15336
 * ```
15337
 *
15338
 * ## Impact on loading templates
15339
 *
15340
 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
15341
 * `templateUrl`'s specified by {@link guide/directive directives}.
15342
 *
15343
 * By default, Angular only loads templates from the same domain and protocol as the application
15344
 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
15345
 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
15346
 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
15347
 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
15348
 *
15349
 * *Please note*:
15350
 * The browser's
15351
 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
15352
 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
15353
 * policy apply in addition to this and may further restrict whether the template is successfully
15354
 * loaded.  This means that without the right CORS policy, loading templates from a different domain
15355
 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
15356
 * browsers.
15357
 *
15358
 * ## This feels like too much overhead
15359
 *
15360
 * It's important to remember that SCE only applies to interpolation expressions.
15361
 *
15362
 * If your expressions are constant literals, they're automatically trusted and you don't need to
15363
 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
15364
 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
15365
 *
15366
 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
15367
 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
15368
 *
15369
 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
15370
 * templates in `ng-include` from your application's domain without having to even know about SCE.
15371
 * It blocks loading templates from other domains or loading templates over http from an https
15372
 * served document.  You can change these by setting your own custom {@link
15373
 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
15374
 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
15375
 *
15376
 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
15377
 * application that's secure and can be audited to verify that with much more ease than bolting
15378
 * security onto an application later.
15379
 *
15380
 * <a name="contexts"></a>
15381
 * ## What trusted context types are supported?
15382
 *
15383
 * | Context             | Notes          |
15384
 * |---------------------|----------------|
15385
 * | `$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. |
15386
 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
15387
 * | `$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. |
15388
 * | `$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. |
15389
 * | `$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. |
15390
 *
15391
 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
15392
 *
15393
 *  Each element in these arrays must be one of the following:
15394
 *
15395
 *  - **'self'**
15396
 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
15397
 *      domain** as the application document using the **same protocol**.
15398
 *  - **String** (except the special value `'self'`)
15399
 *    - The string is matched against the full *normalized / absolute URL* of the resource
15400
 *      being tested (substring matches are not good enough.)
15401
 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
15402
 *      match themselves.
15403
 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
15404
 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use
15405
 *      in a whitelist.
15406
 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
15407
 *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.
15408
 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
15409
 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
15410
 *      http://foo.example.com/templates/**).
15411
 *  - **RegExp** (*see caveat below*)
15412
 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
15413
 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
15414
 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
15415
 *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a
15416
 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
15417
 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
15418
 *      is highly recommended to use the string patterns and only fall back to regular expressions
15419
 *      if they as a last resort.
15420
 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
15421
 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
15422
 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
15423
 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
15424
 *    - If you are generating your JavaScript from some other templating engine (not
15425
 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
15426
 *      remember to escape your regular expression (and be aware that you might need more than
15427
 *      one level of escaping depending on your templating engine and the way you interpolated
15428
 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
15429
 *      enough before coding your own.  e.g. Ruby has
15430
 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
15431
 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
15432
 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
15433
 *      Closure library's [goog.string.regExpEscape(s)](
15434
 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
15435
 *
15436
 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
15437
 *
15438
 * ## Show me an example using SCE.
15439
 *
15440
 * <example module="mySceApp" deps="angular-sanitize.js">
15441
 * <file name="index.html">
15442
 *   <div ng-controller="AppController as myCtrl">
15443
 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
15444
 *     <b>User comments</b><br>
15445
 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
15446
 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
15447
 *     exploit.
15448
 *     <div class="well">
15449
 *       <div ng-repeat="userComment in myCtrl.userComments">
15450
 *         <b>{{userComment.name}}</b>:
15451
 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
15452
 *         <br>
15453
 *       </div>
15454
 *     </div>
15455
 *   </div>
15456
 * </file>
15457
 *
15458
 * <file name="script.js">
15459
 *   angular.module('mySceApp', ['ngSanitize'])
15460
 *     .controller('AppController', ['$http', '$templateCache', '$sce',
15461
 *       function($http, $templateCache, $sce) {
15462
 *         var self = this;
15463
 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
15464
 *           self.userComments = userComments;
15465
 *         });
15466
 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
15467
 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15468
 *             'sanitization.&quot;">Hover over this text.</span>');
15469
 *       }]);
15470
 * </file>
15471
 *
15472
 * <file name="test_data.json">
15473
 * [
15474
 *   { "name": "Alice",
15475
 *     "htmlComment":
15476
 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
15477
 *   },
15478
 *   { "name": "Bob",
15479
 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
15480
 *   }
15481
 * ]
15482
 * </file>
15483
 *
15484
 * <file name="protractor.js" type="protractor">
15485
 *   describe('SCE doc demo', function() {
15486
 *     it('should sanitize untrusted values', function() {
15487
 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
15488
 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
15489
 *     });
15490
 *
15491
 *     it('should NOT sanitize explicitly trusted values', function() {
15492
 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
15493
 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15494
 *           'sanitization.&quot;">Hover over this text.</span>');
15495
 *     });
15496
 *   });
15497
 * </file>
15498
 * </example>
15499
 *
15500
 *
15501
 *
15502
 * ## Can I disable SCE completely?
15503
 *
15504
 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
15505
 * for little coding overhead.  It will be much harder to take an SCE disabled application and
15506
 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
15507
 * for cases where you have a lot of existing code that was written before SCE was introduced and
15508
 * you're migrating them a module at a time.
15509
 *
15510
 * That said, here's how you can completely disable SCE:
15511
 *
15512
 * ```
15513
 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
15514
 *   // Completely disable SCE.  For demonstration purposes only!
15515
 *   // Do not use in new projects.
15516
 *   $sceProvider.enabled(false);
15517
 * });
15518
 * ```
15519
 *
15520
 */
15521
/* jshint maxlen: 100 */
15522
 
15523
function $SceProvider() {
15524
  var enabled = true;
15525
 
15526
  /**
15527
   * @ngdoc method
15528
   * @name $sceProvider#enabled
15529
   * @kind function
15530
   *
15531
   * @param {boolean=} value If provided, then enables/disables SCE.
15532
   * @return {boolean} true if SCE is enabled, false otherwise.
15533
   *
15534
   * @description
15535
   * Enables/disables SCE and returns the current value.
15536
   */
15537
  this.enabled = function(value) {
15538
    if (arguments.length) {
15539
      enabled = !!value;
15540
    }
15541
    return enabled;
15542
  };
15543
 
15544
 
15545
  /* Design notes on the default implementation for SCE.
15546
   *
15547
   * The API contract for the SCE delegate
15548
   * -------------------------------------
15549
   * The SCE delegate object must provide the following 3 methods:
15550
   *
15551
   * - trustAs(contextEnum, value)
15552
   *     This method is used to tell the SCE service that the provided value is OK to use in the
15553
   *     contexts specified by contextEnum.  It must return an object that will be accepted by
15554
   *     getTrusted() for a compatible contextEnum and return this value.
15555
   *
15556
   * - valueOf(value)
15557
   *     For values that were not produced by trustAs(), return them as is.  For values that were
15558
   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
15559
   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
15560
   *     such a value.
15561
   *
15562
   * - getTrusted(contextEnum, value)
15563
   *     This function should return the a value that is safe to use in the context specified by
15564
   *     contextEnum or throw and exception otherwise.
15565
   *
15566
   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
15567
   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
15568
   * instance, an implementation could maintain a registry of all trusted objects by context.  In
15569
   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
15570
   * return the same object passed in if it was found in the registry under a compatible context or
15571
   * throw an exception otherwise.  An implementation might only wrap values some of the time based
15572
   * on some criteria.  getTrusted() might return a value and not throw an exception for special
15573
   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
15574
   *
15575
   *
15576
   * A note on the inheritance model for SCE contexts
15577
   * ------------------------------------------------
15578
   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
15579
   * is purely an implementation details.
15580
   *
15581
   * The contract is simply this:
15582
   *
15583
   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
15584
   *     will also succeed.
15585
   *
15586
   * Inheritance happens to capture this in a natural way.  In some future, we
15587
   * may not use inheritance anymore.  That is OK because no code outside of
15588
   * sce.js and sceSpecs.js would need to be aware of this detail.
15589
   */
15590
 
15591
  this.$get = ['$parse', '$sceDelegate', function(
15592
                $parse,   $sceDelegate) {
15593
    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
15594
    // the "expression(javascript expression)" syntax which is insecure.
15595
    if (enabled && msie < 8) {
15596
      throw $sceMinErr('iequirks',
15597
        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
15598
        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
15599
        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
15600
    }
15601
 
15602
    var sce = shallowCopy(SCE_CONTEXTS);
15603
 
15604
    /**
15605
     * @ngdoc method
15606
     * @name $sce#isEnabled
15607
     * @kind function
15608
     *
15609
     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
15610
     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
15611
     *
15612
     * @description
15613
     * Returns a boolean indicating if SCE is enabled.
15614
     */
15615
    sce.isEnabled = function() {
15616
      return enabled;
15617
    };
15618
    sce.trustAs = $sceDelegate.trustAs;
15619
    sce.getTrusted = $sceDelegate.getTrusted;
15620
    sce.valueOf = $sceDelegate.valueOf;
15621
 
15622
    if (!enabled) {
15623
      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
15624
      sce.valueOf = identity;
15625
    }
15626
 
15627
    /**
15628
     * @ngdoc method
15629
     * @name $sce#parseAs
15630
     *
15631
     * @description
15632
     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
15633
     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
15634
     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
15635
     * *result*)}
15636
     *
15637
     * @param {string} type The kind of SCE context in which this result will be used.
15638
     * @param {string} expression String expression to compile.
15639
     * @returns {function(context, locals)} a function which represents the compiled expression:
15640
     *
15641
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15642
     *      are evaluated against (typically a scope object).
15643
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15644
     *      `context`.
15645
     */
15646
    sce.parseAs = function sceParseAs(type, expr) {
15647
      var parsed = $parse(expr);
15648
      if (parsed.literal && parsed.constant) {
15649
        return parsed;
15650
      } else {
15651
        return $parse(expr, function(value) {
15652
          return sce.getTrusted(type, value);
15653
        });
15654
      }
15655
    };
15656
 
15657
    /**
15658
     * @ngdoc method
15659
     * @name $sce#trustAs
15660
     *
15661
     * @description
15662
     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
15663
     * returns an object that is trusted by angular for use in specified strict contextual
15664
     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
15665
     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
15666
     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
15667
     * escaping.
15668
     *
15669
     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
15670
     *   resource_url, html, js and css.
15671
     * @param {*} value The value that that should be considered trusted/safe.
15672
     * @returns {*} A value that can be used to stand in for the provided `value` in places
15673
     * where Angular expects a $sce.trustAs() return value.
15674
     */
15675
 
15676
    /**
15677
     * @ngdoc method
15678
     * @name $sce#trustAsHtml
15679
     *
15680
     * @description
15681
     * Shorthand method.  `$sce.trustAsHtml(value)` →
15682
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
15683
     *
15684
     * @param {*} value The value to trustAs.
15685
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
15686
     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
15687
     *     only accept expressions that are either literal constants or are the
15688
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15689
     */
15690
 
15691
    /**
15692
     * @ngdoc method
15693
     * @name $sce#trustAsUrl
15694
     *
15695
     * @description
15696
     * Shorthand method.  `$sce.trustAsUrl(value)` →
15697
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
15698
     *
15699
     * @param {*} value The value to trustAs.
15700
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
15701
     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
15702
     *     only accept expressions that are either literal constants or are the
15703
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15704
     */
15705
 
15706
    /**
15707
     * @ngdoc method
15708
     * @name $sce#trustAsResourceUrl
15709
     *
15710
     * @description
15711
     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
15712
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
15713
     *
15714
     * @param {*} value The value to trustAs.
15715
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
15716
     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
15717
     *     only accept expressions that are either literal constants or are the return
15718
     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
15719
     */
15720
 
15721
    /**
15722
     * @ngdoc method
15723
     * @name $sce#trustAsJs
15724
     *
15725
     * @description
15726
     * Shorthand method.  `$sce.trustAsJs(value)` →
15727
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
15728
     *
15729
     * @param {*} value The value to trustAs.
15730
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
15731
     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
15732
     *     only accept expressions that are either literal constants or are the
15733
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15734
     */
15735
 
15736
    /**
15737
     * @ngdoc method
15738
     * @name $sce#getTrusted
15739
     *
15740
     * @description
15741
     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
15742
     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
15743
     * originally supplied value if the queried context type is a supertype of the created type.
15744
     * If this condition isn't satisfied, throws an exception.
15745
     *
15746
     * @param {string} type The kind of context in which this value is to be used.
15747
     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
15748
     *                         call.
15749
     * @returns {*} The value the was originally provided to
15750
     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
15751
     *              Otherwise, throws an exception.
15752
     */
15753
 
15754
    /**
15755
     * @ngdoc method
15756
     * @name $sce#getTrustedHtml
15757
     *
15758
     * @description
15759
     * Shorthand method.  `$sce.getTrustedHtml(value)` →
15760
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
15761
     *
15762
     * @param {*} value The value to pass to `$sce.getTrusted`.
15763
     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
15764
     */
15765
 
15766
    /**
15767
     * @ngdoc method
15768
     * @name $sce#getTrustedCss
15769
     *
15770
     * @description
15771
     * Shorthand method.  `$sce.getTrustedCss(value)` →
15772
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
15773
     *
15774
     * @param {*} value The value to pass to `$sce.getTrusted`.
15775
     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
15776
     */
15777
 
15778
    /**
15779
     * @ngdoc method
15780
     * @name $sce#getTrustedUrl
15781
     *
15782
     * @description
15783
     * Shorthand method.  `$sce.getTrustedUrl(value)` →
15784
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
15785
     *
15786
     * @param {*} value The value to pass to `$sce.getTrusted`.
15787
     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
15788
     */
15789
 
15790
    /**
15791
     * @ngdoc method
15792
     * @name $sce#getTrustedResourceUrl
15793
     *
15794
     * @description
15795
     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
15796
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
15797
     *
15798
     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
15799
     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
15800
     */
15801
 
15802
    /**
15803
     * @ngdoc method
15804
     * @name $sce#getTrustedJs
15805
     *
15806
     * @description
15807
     * Shorthand method.  `$sce.getTrustedJs(value)` →
15808
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
15809
     *
15810
     * @param {*} value The value to pass to `$sce.getTrusted`.
15811
     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
15812
     */
15813
 
15814
    /**
15815
     * @ngdoc method
15816
     * @name $sce#parseAsHtml
15817
     *
15818
     * @description
15819
     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
15820
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
15821
     *
15822
     * @param {string} expression String expression to compile.
15823
     * @returns {function(context, locals)} a function which represents the compiled expression:
15824
     *
15825
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15826
     *      are evaluated against (typically a scope object).
15827
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15828
     *      `context`.
15829
     */
15830
 
15831
    /**
15832
     * @ngdoc method
15833
     * @name $sce#parseAsCss
15834
     *
15835
     * @description
15836
     * Shorthand method.  `$sce.parseAsCss(value)` →
15837
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
15838
     *
15839
     * @param {string} expression String expression to compile.
15840
     * @returns {function(context, locals)} a function which represents the compiled expression:
15841
     *
15842
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15843
     *      are evaluated against (typically a scope object).
15844
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15845
     *      `context`.
15846
     */
15847
 
15848
    /**
15849
     * @ngdoc method
15850
     * @name $sce#parseAsUrl
15851
     *
15852
     * @description
15853
     * Shorthand method.  `$sce.parseAsUrl(value)` →
15854
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
15855
     *
15856
     * @param {string} expression String expression to compile.
15857
     * @returns {function(context, locals)} a function which represents the compiled expression:
15858
     *
15859
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15860
     *      are evaluated against (typically a scope object).
15861
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15862
     *      `context`.
15863
     */
15864
 
15865
    /**
15866
     * @ngdoc method
15867
     * @name $sce#parseAsResourceUrl
15868
     *
15869
     * @description
15870
     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
15871
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
15872
     *
15873
     * @param {string} expression String expression to compile.
15874
     * @returns {function(context, locals)} a function which represents the compiled expression:
15875
     *
15876
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15877
     *      are evaluated against (typically a scope object).
15878
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15879
     *      `context`.
15880
     */
15881
 
15882
    /**
15883
     * @ngdoc method
15884
     * @name $sce#parseAsJs
15885
     *
15886
     * @description
15887
     * Shorthand method.  `$sce.parseAsJs(value)` →
15888
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
15889
     *
15890
     * @param {string} expression String expression to compile.
15891
     * @returns {function(context, locals)} a function which represents the compiled expression:
15892
     *
15893
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15894
     *      are evaluated against (typically a scope object).
15895
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15896
     *      `context`.
15897
     */
15898
 
15899
    // Shorthand delegations.
15900
    var parse = sce.parseAs,
15901
        getTrusted = sce.getTrusted,
15902
        trustAs = sce.trustAs;
15903
 
15904
    forEach(SCE_CONTEXTS, function(enumValue, name) {
15905
      var lName = lowercase(name);
15906
      sce[camelCase("parse_as_" + lName)] = function(expr) {
15907
        return parse(enumValue, expr);
15908
      };
15909
      sce[camelCase("get_trusted_" + lName)] = function(value) {
15910
        return getTrusted(enumValue, value);
15911
      };
15912
      sce[camelCase("trust_as_" + lName)] = function(value) {
15913
        return trustAs(enumValue, value);
15914
      };
15915
    });
15916
 
15917
    return sce;
15918
  }];
15919
}
15920
 
15921
/**
15922
 * !!! This is an undocumented "private" service !!!
15923
 *
15924
 * @name $sniffer
15925
 * @requires $window
15926
 * @requires $document
15927
 *
15928
 * @property {boolean} history Does the browser support html5 history api ?
15929
 * @property {boolean} transitions Does the browser support CSS transition events ?
15930
 * @property {boolean} animations Does the browser support CSS animation events ?
15931
 *
15932
 * @description
15933
 * This is very simple implementation of testing browser's features.
15934
 */
15935
function $SnifferProvider() {
15936
  this.$get = ['$window', '$document', function($window, $document) {
15937
    var eventSupport = {},
15938
        android =
15939
          int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
15940
        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
15941
        document = $document[0] || {},
15942
        vendorPrefix,
15943
        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
15944
        bodyStyle = document.body && document.body.style,
15945
        transitions = false,
15946
        animations = false,
15947
        match;
15948
 
15949
    if (bodyStyle) {
15950
      for (var prop in bodyStyle) {
15951
        if (match = vendorRegex.exec(prop)) {
15952
          vendorPrefix = match[0];
15953
          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
15954
          break;
15955
        }
15956
      }
15957
 
15958
      if (!vendorPrefix) {
15959
        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
15960
      }
15961
 
15962
      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
15963
      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
15964
 
15965
      if (android && (!transitions ||  !animations)) {
15966
        transitions = isString(document.body.style.webkitTransition);
15967
        animations = isString(document.body.style.webkitAnimation);
15968
      }
15969
    }
15970
 
15971
 
15972
    return {
15973
      // Android has history.pushState, but it does not update location correctly
15974
      // so let's not use the history API at all.
15975
      // http://code.google.com/p/android/issues/detail?id=17471
15976
      // https://github.com/angular/angular.js/issues/904
15977
 
15978
      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
15979
      // so let's not use the history API also
15980
      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
15981
      // jshint -W018
15982
      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
15983
      // jshint +W018
15984
      hasEvent: function(event) {
15985
        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
15986
        // it. In particular the event is not fired when backspace or delete key are pressed or
15987
        // when cut operation is performed.
15988
        // IE10+ implements 'input' event but it erroneously fires under various situations,
15989
        // e.g. when placeholder changes, or a form is focused.
15990
        if (event === 'input' && msie <= 11) return false;
15991
 
15992
        if (isUndefined(eventSupport[event])) {
15993
          var divElm = document.createElement('div');
15994
          eventSupport[event] = 'on' + event in divElm;
15995
        }
15996
 
15997
        return eventSupport[event];
15998
      },
15999
      csp: csp(),
16000
      vendorPrefix: vendorPrefix,
16001
      transitions: transitions,
16002
      animations: animations,
16003
      android: android
16004
    };
16005
  }];
16006
}
16007
 
16008
var $compileMinErr = minErr('$compile');
16009
 
16010
/**
16011
 * @ngdoc service
16012
 * @name $templateRequest
16013
 *
16014
 * @description
16015
 * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
16016
 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
16017
 * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
16018
 * by setting the 2nd parameter of the function to true).
16019
 *
16020
 * @param {string} tpl The HTTP request template URL
16021
 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
16022
 *
16023
 * @return {Promise} the HTTP Promise for the given.
16024
 *
16025
 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
16026
 */
16027
function $TemplateRequestProvider() {
16028
  this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
16029
    function handleRequestFn(tpl, ignoreRequestError) {
16030
      handleRequestFn.totalPendingRequests++;
16031
 
16032
      var transformResponse = $http.defaults && $http.defaults.transformResponse;
16033
 
16034
      if (isArray(transformResponse)) {
16035
        transformResponse = transformResponse.filter(function(transformer) {
16036
          return transformer !== defaultHttpResponseTransform;
16037
        });
16038
      } else if (transformResponse === defaultHttpResponseTransform) {
16039
        transformResponse = null;
16040
      }
16041
 
16042
      var httpOptions = {
16043
        cache: $templateCache,
16044
        transformResponse: transformResponse
16045
      };
16046
 
16047
      return $http.get(tpl, httpOptions)
16048
        ['finally'](function() {
16049
          handleRequestFn.totalPendingRequests--;
16050
        })
16051
        .then(function(response) {
16052
          return response.data;
16053
        }, handleError);
16054
 
16055
      function handleError(resp) {
16056
        if (!ignoreRequestError) {
16057
          throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
16058
        }
16059
        return $q.reject(resp);
16060
      }
16061
    }
16062
 
16063
    handleRequestFn.totalPendingRequests = 0;
16064
 
16065
    return handleRequestFn;
16066
  }];
16067
}
16068
 
16069
function $$TestabilityProvider() {
16070
  this.$get = ['$rootScope', '$browser', '$location',
16071
       function($rootScope,   $browser,   $location) {
16072
 
16073
    /**
16074
     * @name $testability
16075
     *
16076
     * @description
16077
     * The private $$testability service provides a collection of methods for use when debugging
16078
     * or by automated test and debugging tools.
16079
     */
16080
    var testability = {};
16081
 
16082
    /**
16083
     * @name $$testability#findBindings
16084
     *
16085
     * @description
16086
     * Returns an array of elements that are bound (via ng-bind or {{}})
16087
     * to expressions matching the input.
16088
     *
16089
     * @param {Element} element The element root to search from.
16090
     * @param {string} expression The binding expression to match.
16091
     * @param {boolean} opt_exactMatch If true, only returns exact matches
16092
     *     for the expression. Filters and whitespace are ignored.
16093
     */
16094
    testability.findBindings = function(element, expression, opt_exactMatch) {
16095
      var bindings = element.getElementsByClassName('ng-binding');
16096
      var matches = [];
16097
      forEach(bindings, function(binding) {
16098
        var dataBinding = angular.element(binding).data('$binding');
16099
        if (dataBinding) {
16100
          forEach(dataBinding, function(bindingName) {
16101
            if (opt_exactMatch) {
16102
              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
16103
              if (matcher.test(bindingName)) {
16104
                matches.push(binding);
16105
              }
16106
            } else {
16107
              if (bindingName.indexOf(expression) != -1) {
16108
                matches.push(binding);
16109
              }
16110
            }
16111
          });
16112
        }
16113
      });
16114
      return matches;
16115
    };
16116
 
16117
    /**
16118
     * @name $$testability#findModels
16119
     *
16120
     * @description
16121
     * Returns an array of elements that are two-way found via ng-model to
16122
     * expressions matching the input.
16123
     *
16124
     * @param {Element} element The element root to search from.
16125
     * @param {string} expression The model expression to match.
16126
     * @param {boolean} opt_exactMatch If true, only returns exact matches
16127
     *     for the expression.
16128
     */
16129
    testability.findModels = function(element, expression, opt_exactMatch) {
16130
      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
16131
      for (var p = 0; p < prefixes.length; ++p) {
16132
        var attributeEquals = opt_exactMatch ? '=' : '*=';
16133
        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
16134
        var elements = element.querySelectorAll(selector);
16135
        if (elements.length) {
16136
          return elements;
16137
        }
16138
      }
16139
    };
16140
 
16141
    /**
16142
     * @name $$testability#getLocation
16143
     *
16144
     * @description
16145
     * Shortcut for getting the location in a browser agnostic way. Returns
16146
     *     the path, search, and hash. (e.g. /path?a=b#hash)
16147
     */
16148
    testability.getLocation = function() {
16149
      return $location.url();
16150
    };
16151
 
16152
    /**
16153
     * @name $$testability#setLocation
16154
     *
16155
     * @description
16156
     * Shortcut for navigating to a location without doing a full page reload.
16157
     *
16158
     * @param {string} url The location url (path, search and hash,
16159
     *     e.g. /path?a=b#hash) to go to.
16160
     */
16161
    testability.setLocation = function(url) {
16162
      if (url !== $location.url()) {
16163
        $location.url(url);
16164
        $rootScope.$digest();
16165
      }
16166
    };
16167
 
16168
    /**
16169
     * @name $$testability#whenStable
16170
     *
16171
     * @description
16172
     * Calls the callback when $timeout and $http requests are completed.
16173
     *
16174
     * @param {function} callback
16175
     */
16176
    testability.whenStable = function(callback) {
16177
      $browser.notifyWhenNoOutstandingRequests(callback);
16178
    };
16179
 
16180
    return testability;
16181
  }];
16182
}
16183
 
16184
function $TimeoutProvider() {
16185
  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
16186
       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
16187
    var deferreds = {};
16188
 
16189
 
16190
     /**
16191
      * @ngdoc service
16192
      * @name $timeout
16193
      *
16194
      * @description
16195
      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16196
      * block and delegates any exceptions to
16197
      * {@link ng.$exceptionHandler $exceptionHandler} service.
16198
      *
16199
      * The return value of registering a timeout function is a promise, which will be resolved when
16200
      * the timeout is reached and the timeout function is executed.
16201
      *
16202
      * To cancel a timeout request, call `$timeout.cancel(promise)`.
16203
      *
16204
      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
16205
      * synchronously flush the queue of deferred functions.
16206
      *
16207
      * @param {function()} fn A function, whose execution should be delayed.
16208
      * @param {number=} [delay=0] Delay in milliseconds.
16209
      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
16210
      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
16211
      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
16212
      *   promise will be resolved with is the return value of the `fn` function.
16213
      *
16214
      */
16215
    function timeout(fn, delay, invokeApply) {
16216
      var skipApply = (isDefined(invokeApply) && !invokeApply),
16217
          deferred = (skipApply ? $$q : $q).defer(),
16218
          promise = deferred.promise,
16219
          timeoutId;
16220
 
16221
      timeoutId = $browser.defer(function() {
16222
        try {
16223
          deferred.resolve(fn());
16224
        } catch (e) {
16225
          deferred.reject(e);
16226
          $exceptionHandler(e);
16227
        }
16228
        finally {
16229
          delete deferreds[promise.$$timeoutId];
16230
        }
16231
 
16232
        if (!skipApply) $rootScope.$apply();
16233
      }, delay);
16234
 
16235
      promise.$$timeoutId = timeoutId;
16236
      deferreds[timeoutId] = deferred;
16237
 
16238
      return promise;
16239
    }
16240
 
16241
 
16242
     /**
16243
      * @ngdoc method
16244
      * @name $timeout#cancel
16245
      *
16246
      * @description
16247
      * Cancels a task associated with the `promise`. As a result of this, the promise will be
16248
      * resolved with a rejection.
16249
      *
16250
      * @param {Promise=} promise Promise returned by the `$timeout` function.
16251
      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
16252
      *   canceled.
16253
      */
16254
    timeout.cancel = function(promise) {
16255
      if (promise && promise.$$timeoutId in deferreds) {
16256
        deferreds[promise.$$timeoutId].reject('canceled');
16257
        delete deferreds[promise.$$timeoutId];
16258
        return $browser.defer.cancel(promise.$$timeoutId);
16259
      }
16260
      return false;
16261
    };
16262
 
16263
    return timeout;
16264
  }];
16265
}
16266
 
16267
// NOTE:  The usage of window and document instead of $window and $document here is
16268
// deliberate.  This service depends on the specific behavior of anchor nodes created by the
16269
// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
16270
// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
16271
// doesn't know about mocked locations and resolves URLs to the real document - which is
16272
// exactly the behavior needed here.  There is little value is mocking these out for this
16273
// service.
16274
var urlParsingNode = document.createElement("a");
16275
var originUrl = urlResolve(window.location.href);
16276
 
16277
 
16278
/**
16279
 *
16280
 * Implementation Notes for non-IE browsers
16281
 * ----------------------------------------
16282
 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
16283
 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
16284
 * URL will be resolved into an absolute URL in the context of the application document.
16285
 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
16286
 * properties are all populated to reflect the normalized URL.  This approach has wide
16287
 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
16288
 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16289
 *
16290
 * Implementation Notes for IE
16291
 * ---------------------------
16292
 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
16293
 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
16294
 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
16295
 * work around that by performing the parsing in a 2nd step by taking a previously normalized
16296
 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
16297
 * properties such as protocol, hostname, port, etc.
16298
 *
16299
 * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one
16300
 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
16301
 * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.
16302
 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
16303
 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
16304
 * method and IE < 8 is unsupported.
16305
 *
16306
 * References:
16307
 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
16308
 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16309
 *   http://url.spec.whatwg.org/#urlutils
16310
 *   https://github.com/angular/angular.js/pull/2902
16311
 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
16312
 *
16313
 * @kind function
16314
 * @param {string} url The URL to be parsed.
16315
 * @description Normalizes and parses a URL.
16316
 * @returns {object} Returns the normalized URL as a dictionary.
16317
 *
16318
 *   | member name   | Description    |
16319
 *   |---------------|----------------|
16320
 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
16321
 *   | protocol      | The protocol including the trailing colon                              |
16322
 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
16323
 *   | search        | The search params, minus the question mark                             |
16324
 *   | hash          | The hash string, minus the hash symbol
16325
 *   | hostname      | The hostname
16326
 *   | port          | The port, without ":"
16327
 *   | pathname      | The pathname, beginning with "/"
16328
 *
16329
 */
16330
function urlResolve(url) {
16331
  var href = url;
16332
 
16333
  if (msie) {
16334
    // Normalize before parse.  Refer Implementation Notes on why this is
16335
    // done in two steps on IE.
16336
    urlParsingNode.setAttribute("href", href);
16337
    href = urlParsingNode.href;
16338
  }
16339
 
16340
  urlParsingNode.setAttribute('href', href);
16341
 
16342
  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
16343
  return {
16344
    href: urlParsingNode.href,
16345
    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
16346
    host: urlParsingNode.host,
16347
    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
16348
    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
16349
    hostname: urlParsingNode.hostname,
16350
    port: urlParsingNode.port,
16351
    pathname: (urlParsingNode.pathname.charAt(0) === '/')
16352
      ? urlParsingNode.pathname
16353
      : '/' + urlParsingNode.pathname
16354
  };
16355
}
16356
 
16357
/**
16358
 * Parse a request URL and determine whether this is a same-origin request as the application document.
16359
 *
16360
 * @param {string|object} requestUrl The url of the request as a string that will be resolved
16361
 * or a parsed URL object.
16362
 * @returns {boolean} Whether the request is for the same origin as the application document.
16363
 */
16364
function urlIsSameOrigin(requestUrl) {
16365
  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
16366
  return (parsed.protocol === originUrl.protocol &&
16367
          parsed.host === originUrl.host);
16368
}
16369
 
16370
/**
16371
 * @ngdoc service
16372
 * @name $window
16373
 *
16374
 * @description
16375
 * A reference to the browser's `window` object. While `window`
16376
 * is globally available in JavaScript, it causes testability problems, because
16377
 * it is a global variable. In angular we always refer to it through the
16378
 * `$window` service, so it may be overridden, removed or mocked for testing.
16379
 *
16380
 * Expressions, like the one defined for the `ngClick` directive in the example
16381
 * below, are evaluated with respect to the current scope.  Therefore, there is
16382
 * no risk of inadvertently coding in a dependency on a global value in such an
16383
 * expression.
16384
 *
16385
 * @example
16386
   <example module="windowExample">
16387
     <file name="index.html">
16388
       <script>
16389
         angular.module('windowExample', [])
16390
           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
16391
             $scope.greeting = 'Hello, World!';
16392
             $scope.doGreeting = function(greeting) {
16393
               $window.alert(greeting);
16394
             };
16395
           }]);
16396
       </script>
16397
       <div ng-controller="ExampleController">
16398
         <input type="text" ng-model="greeting" />
16399
         <button ng-click="doGreeting(greeting)">ALERT</button>
16400
       </div>
16401
     </file>
16402
     <file name="protractor.js" type="protractor">
16403
      it('should display the greeting in the input box', function() {
16404
       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
16405
       // If we click the button it will block the test runner
16406
       // element(':button').click();
16407
      });
16408
     </file>
16409
   </example>
16410
 */
16411
function $WindowProvider() {
16412
  this.$get = valueFn(window);
16413
}
16414
 
16415
/* global currencyFilter: true,
16416
 dateFilter: true,
16417
 filterFilter: true,
16418
 jsonFilter: true,
16419
 limitToFilter: true,
16420
 lowercaseFilter: true,
16421
 numberFilter: true,
16422
 orderByFilter: true,
16423
 uppercaseFilter: true,
16424
 */
16425
 
16426
/**
16427
 * @ngdoc provider
16428
 * @name $filterProvider
16429
 * @description
16430
 *
16431
 * Filters are just functions which transform input to an output. However filters need to be
16432
 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
16433
 * annotated with dependencies and is responsible for creating a filter function.
16434
 *
16435
 * ```js
16436
 *   // Filter registration
16437
 *   function MyModule($provide, $filterProvider) {
16438
 *     // create a service to demonstrate injection (not always needed)
16439
 *     $provide.value('greet', function(name){
16440
 *       return 'Hello ' + name + '!';
16441
 *     });
16442
 *
16443
 *     // register a filter factory which uses the
16444
 *     // greet service to demonstrate DI.
16445
 *     $filterProvider.register('greet', function(greet){
16446
 *       // return the filter function which uses the greet service
16447
 *       // to generate salutation
16448
 *       return function(text) {
16449
 *         // filters need to be forgiving so check input validity
16450
 *         return text && greet(text) || text;
16451
 *       };
16452
 *     });
16453
 *   }
16454
 * ```
16455
 *
16456
 * The filter function is registered with the `$injector` under the filter name suffix with
16457
 * `Filter`.
16458
 *
16459
 * ```js
16460
 *   it('should be the same instance', inject(
16461
 *     function($filterProvider) {
16462
 *       $filterProvider.register('reverse', function(){
16463
 *         return ...;
16464
 *       });
16465
 *     },
16466
 *     function($filter, reverseFilter) {
16467
 *       expect($filter('reverse')).toBe(reverseFilter);
16468
 *     });
16469
 * ```
16470
 *
16471
 *
16472
 * For more information about how angular filters work, and how to create your own filters, see
16473
 * {@link guide/filter Filters} in the Angular Developer Guide.
16474
 */
16475
 
16476
/**
16477
 * @ngdoc service
16478
 * @name $filter
16479
 * @kind function
16480
 * @description
16481
 * Filters are used for formatting data displayed to the user.
16482
 *
16483
 * The general syntax in templates is as follows:
16484
 *
16485
 *         {{ expression [| filter_name[:parameter_value] ... ] }}
16486
 *
16487
 * @param {String} name Name of the filter function to retrieve
16488
 * @return {Function} the filter function
16489
 * @example
16490
   <example name="$filter" module="filterExample">
16491
     <file name="index.html">
16492
       <div ng-controller="MainCtrl">
16493
        <h3>{{ originalText }}</h3>
16494
        <h3>{{ filteredText }}</h3>
16495
       </div>
16496
     </file>
16497
 
16498
     <file name="script.js">
16499
      angular.module('filterExample', [])
16500
      .controller('MainCtrl', function($scope, $filter) {
16501
        $scope.originalText = 'hello';
16502
        $scope.filteredText = $filter('uppercase')($scope.originalText);
16503
      });
16504
     </file>
16505
   </example>
16506
  */
16507
$FilterProvider.$inject = ['$provide'];
16508
function $FilterProvider($provide) {
16509
  var suffix = 'Filter';
16510
 
16511
  /**
16512
   * @ngdoc method
16513
   * @name $filterProvider#register
16514
   * @param {string|Object} name Name of the filter function, or an object map of filters where
16515
   *    the keys are the filter names and the values are the filter factories.
16516
   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
16517
   *    of the registered filter instances.
16518
   */
16519
  function register(name, factory) {
16520
    if (isObject(name)) {
16521
      var filters = {};
16522
      forEach(name, function(filter, key) {
16523
        filters[key] = register(key, filter);
16524
      });
16525
      return filters;
16526
    } else {
16527
      return $provide.factory(name + suffix, factory);
16528
    }
16529
  }
16530
  this.register = register;
16531
 
16532
  this.$get = ['$injector', function($injector) {
16533
    return function(name) {
16534
      return $injector.get(name + suffix);
16535
    };
16536
  }];
16537
 
16538
  ////////////////////////////////////////
16539
 
16540
  /* global
16541
    currencyFilter: false,
16542
    dateFilter: false,
16543
    filterFilter: false,
16544
    jsonFilter: false,
16545
    limitToFilter: false,
16546
    lowercaseFilter: false,
16547
    numberFilter: false,
16548
    orderByFilter: false,
16549
    uppercaseFilter: false,
16550
  */
16551
 
16552
  register('currency', currencyFilter);
16553
  register('date', dateFilter);
16554
  register('filter', filterFilter);
16555
  register('json', jsonFilter);
16556
  register('limitTo', limitToFilter);
16557
  register('lowercase', lowercaseFilter);
16558
  register('number', numberFilter);
16559
  register('orderBy', orderByFilter);
16560
  register('uppercase', uppercaseFilter);
16561
}
16562
 
16563
/**
16564
 * @ngdoc filter
16565
 * @name filter
16566
 * @kind function
16567
 *
16568
 * @description
16569
 * Selects a subset of items from `array` and returns it as a new array.
16570
 *
16571
 * @param {Array} array The source array.
16572
 * @param {string|Object|function()} expression The predicate to be used for selecting items from
16573
 *   `array`.
16574
 *
16575
 *   Can be one of:
16576
 *
16577
 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
16578
 *     objects with string properties in `array` that match this string will be returned. This also
16579
 *     applies to nested object properties.
16580
 *     The predicate can be negated by prefixing the string with `!`.
16581
 *
16582
 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
16583
 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
16584
 *     which have property `name` containing "M" and property `phone` containing "1". A special
16585
 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
16586
 *     property of the object or its nested object properties. That's equivalent to the simple
16587
 *     substring match with a `string` as described above. The predicate can be negated by prefixing
16588
 *     the string with `!`.
16589
 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
16590
 *     not containing "M".
16591
 *
16592
 *     Note that a named property will match properties on the same level only, while the special
16593
 *     `$` property will match properties on the same level or deeper. E.g. an array item like
16594
 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
16595
 *     **will** be matched by `{$: 'John'}`.
16596
 *
16597
 *   - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
16598
 *     function is called for each element of `array`. The final result is an array of those
16599
 *     elements that the predicate returned true for.
16600
 *
16601
 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
16602
 *     determining if the expected value (from the filter expression) and actual value (from
16603
 *     the object in the array) should be considered a match.
16604
 *
16605
 *   Can be one of:
16606
 *
16607
 *   - `function(actual, expected)`:
16608
 *     The function will be given the object value and the predicate value to compare and
16609
 *     should return true if both values should be considered equal.
16610
 *
16611
 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
16612
 *     This is essentially strict comparison of expected and actual.
16613
 *
16614
 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
16615
 *     insensitive way.
16616
 *
16617
 * @example
16618
   <example>
16619
     <file name="index.html">
16620
       <div ng-init="friends = [{name:'John', phone:'555-1276'},
16621
                                {name:'Mary', phone:'800-BIG-MARY'},
16622
                                {name:'Mike', phone:'555-4321'},
16623
                                {name:'Adam', phone:'555-5678'},
16624
                                {name:'Julie', phone:'555-8765'},
16625
                                {name:'Juliette', phone:'555-5678'}]"></div>
16626
 
16627
       Search: <input ng-model="searchText">
16628
       <table id="searchTextResults">
16629
         <tr><th>Name</th><th>Phone</th></tr>
16630
         <tr ng-repeat="friend in friends | filter:searchText">
16631
           <td>{{friend.name}}</td>
16632
           <td>{{friend.phone}}</td>
16633
         </tr>
16634
       </table>
16635
       <hr>
16636
       Any: <input ng-model="search.$"> <br>
16637
       Name only <input ng-model="search.name"><br>
16638
       Phone only <input ng-model="search.phone"><br>
16639
       Equality <input type="checkbox" ng-model="strict"><br>
16640
       <table id="searchObjResults">
16641
         <tr><th>Name</th><th>Phone</th></tr>
16642
         <tr ng-repeat="friendObj in friends | filter:search:strict">
16643
           <td>{{friendObj.name}}</td>
16644
           <td>{{friendObj.phone}}</td>
16645
         </tr>
16646
       </table>
16647
     </file>
16648
     <file name="protractor.js" type="protractor">
16649
       var expectFriendNames = function(expectedNames, key) {
16650
         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
16651
           arr.forEach(function(wd, i) {
16652
             expect(wd.getText()).toMatch(expectedNames[i]);
16653
           });
16654
         });
16655
       };
16656
 
16657
       it('should search across all fields when filtering with a string', function() {
16658
         var searchText = element(by.model('searchText'));
16659
         searchText.clear();
16660
         searchText.sendKeys('m');
16661
         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
16662
 
16663
         searchText.clear();
16664
         searchText.sendKeys('76');
16665
         expectFriendNames(['John', 'Julie'], 'friend');
16666
       });
16667
 
16668
       it('should search in specific fields when filtering with a predicate object', function() {
16669
         var searchAny = element(by.model('search.$'));
16670
         searchAny.clear();
16671
         searchAny.sendKeys('i');
16672
         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
16673
       });
16674
       it('should use a equal comparison when comparator is true', function() {
16675
         var searchName = element(by.model('search.name'));
16676
         var strict = element(by.model('strict'));
16677
         searchName.clear();
16678
         searchName.sendKeys('Julie');
16679
         strict.click();
16680
         expectFriendNames(['Julie'], 'friendObj');
16681
       });
16682
     </file>
16683
   </example>
16684
 */
16685
function filterFilter() {
16686
  return function(array, expression, comparator) {
16687
    if (!isArray(array)) return array;
16688
 
16689
    var predicateFn;
16690
    var matchAgainstAnyProp;
16691
 
16692
    switch (typeof expression) {
16693
      case 'function':
16694
        predicateFn = expression;
16695
        break;
16696
      case 'boolean':
16697
      case 'number':
16698
      case 'string':
16699
        matchAgainstAnyProp = true;
16700
        //jshint -W086
16701
      case 'object':
16702
        //jshint +W086
16703
        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
16704
        break;
16705
      default:
16706
        return array;
16707
    }
16708
 
16709
    return array.filter(predicateFn);
16710
  };
16711
}
16712
 
16713
// Helper functions for `filterFilter`
16714
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
16715
  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
16716
  var predicateFn;
16717
 
16718
  if (comparator === true) {
16719
    comparator = equals;
16720
  } else if (!isFunction(comparator)) {
16721
    comparator = function(actual, expected) {
16722
      if (isObject(actual) || isObject(expected)) {
16723
        // Prevent an object to be considered equal to a string like `'[object'`
16724
        return false;
16725
      }
16726
 
16727
      actual = lowercase('' + actual);
16728
      expected = lowercase('' + expected);
16729
      return actual.indexOf(expected) !== -1;
16730
    };
16731
  }
16732
 
16733
  predicateFn = function(item) {
16734
    if (shouldMatchPrimitives && !isObject(item)) {
16735
      return deepCompare(item, expression.$, comparator, false);
16736
    }
16737
    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
16738
  };
16739
 
16740
  return predicateFn;
16741
}
16742
 
16743
function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
16744
  var actualType = typeof actual;
16745
  var expectedType = typeof expected;
16746
 
16747
  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
16748
    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
16749
  } else if (isArray(actual)) {
16750
    // In case `actual` is an array, consider it a match
16751
    // if ANY of it's items matches `expected`
16752
    return actual.some(function(item) {
16753
      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
16754
    });
16755
  }
16756
 
16757
  switch (actualType) {
16758
    case 'object':
16759
      var key;
16760
      if (matchAgainstAnyProp) {
16761
        for (key in actual) {
16762
          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
16763
            return true;
16764
          }
16765
        }
16766
        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
16767
      } else if (expectedType === 'object') {
16768
        for (key in expected) {
16769
          var expectedVal = expected[key];
16770
          if (isFunction(expectedVal)) {
16771
            continue;
16772
          }
16773
 
16774
          var matchAnyProperty = key === '$';
16775
          var actualVal = matchAnyProperty ? actual : actual[key];
16776
          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
16777
            return false;
16778
          }
16779
        }
16780
        return true;
16781
      } else {
16782
        return comparator(actual, expected);
16783
      }
16784
      break;
16785
    case 'function':
16786
      return false;
16787
    default:
16788
      return comparator(actual, expected);
16789
  }
16790
}
16791
 
16792
/**
16793
 * @ngdoc filter
16794
 * @name currency
16795
 * @kind function
16796
 *
16797
 * @description
16798
 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
16799
 * symbol for current locale is used.
16800
 *
16801
 * @param {number} amount Input to filter.
16802
 * @param {string=} symbol Currency symbol or identifier to be displayed.
16803
 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
16804
 * @returns {string} Formatted number.
16805
 *
16806
 *
16807
 * @example
16808
   <example module="currencyExample">
16809
     <file name="index.html">
16810
       <script>
16811
         angular.module('currencyExample', [])
16812
           .controller('ExampleController', ['$scope', function($scope) {
16813
             $scope.amount = 1234.56;
16814
           }]);
16815
       </script>
16816
       <div ng-controller="ExampleController">
16817
         <input type="number" ng-model="amount"> <br>
16818
         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
16819
         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
16820
         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
16821
       </div>
16822
     </file>
16823
     <file name="protractor.js" type="protractor">
16824
       it('should init with 1234.56', function() {
16825
         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
16826
         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
16827
         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
16828
       });
16829
       it('should update', function() {
16830
         if (browser.params.browser == 'safari') {
16831
           // Safari does not understand the minus key. See
16832
           // https://github.com/angular/protractor/issues/481
16833
           return;
16834
         }
16835
         element(by.model('amount')).clear();
16836
         element(by.model('amount')).sendKeys('-1234');
16837
         expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
16838
         expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
16839
         expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
16840
       });
16841
     </file>
16842
   </example>
16843
 */
16844
currencyFilter.$inject = ['$locale'];
16845
function currencyFilter($locale) {
16846
  var formats = $locale.NUMBER_FORMATS;
16847
  return function(amount, currencySymbol, fractionSize) {
16848
    if (isUndefined(currencySymbol)) {
16849
      currencySymbol = formats.CURRENCY_SYM;
16850
    }
16851
 
16852
    if (isUndefined(fractionSize)) {
16853
      fractionSize = formats.PATTERNS[1].maxFrac;
16854
    }
16855
 
16856
    // if null or undefined pass it through
16857
    return (amount == null)
16858
        ? amount
16859
        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
16860
            replace(/\u00A4/g, currencySymbol);
16861
  };
16862
}
16863
 
16864
/**
16865
 * @ngdoc filter
16866
 * @name number
16867
 * @kind function
16868
 *
16869
 * @description
16870
 * Formats a number as text.
16871
 *
16872
 * If the input is not a number an empty string is returned.
16873
 *
16874
 * @param {number|string} number Number to format.
16875
 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
16876
 * If this is not provided then the fraction size is computed from the current locale's number
16877
 * formatting pattern. In the case of the default locale, it will be 3.
16878
 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
16879
 *
16880
 * @example
16881
   <example module="numberFilterExample">
16882
     <file name="index.html">
16883
       <script>
16884
         angular.module('numberFilterExample', [])
16885
           .controller('ExampleController', ['$scope', function($scope) {
16886
             $scope.val = 1234.56789;
16887
           }]);
16888
       </script>
16889
       <div ng-controller="ExampleController">
16890
         Enter number: <input ng-model='val'><br>
16891
         Default formatting: <span id='number-default'>{{val | number}}</span><br>
16892
         No fractions: <span>{{val | number:0}}</span><br>
16893
         Negative number: <span>{{-val | number:4}}</span>
16894
       </div>
16895
     </file>
16896
     <file name="protractor.js" type="protractor">
16897
       it('should format numbers', function() {
16898
         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
16899
         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
16900
         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
16901
       });
16902
 
16903
       it('should update', function() {
16904
         element(by.model('val')).clear();
16905
         element(by.model('val')).sendKeys('3374.333');
16906
         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
16907
         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
16908
         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
16909
      });
16910
     </file>
16911
   </example>
16912
 */
16913
 
16914
 
16915
numberFilter.$inject = ['$locale'];
16916
function numberFilter($locale) {
16917
  var formats = $locale.NUMBER_FORMATS;
16918
  return function(number, fractionSize) {
16919
 
16920
    // if null or undefined pass it through
16921
    return (number == null)
16922
        ? number
16923
        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
16924
                       fractionSize);
16925
  };
16926
}
16927
 
16928
var DECIMAL_SEP = '.';
16929
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16930
  if (!isFinite(number) || isObject(number)) return '';
16931
 
16932
  var isNegative = number < 0;
16933
  number = Math.abs(number);
16934
  var numStr = number + '',
16935
      formatedText = '',
16936
      parts = [];
16937
 
16938
  var hasExponent = false;
16939
  if (numStr.indexOf('e') !== -1) {
16940
    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
16941
    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
16942
      number = 0;
16943
    } else {
16944
      formatedText = numStr;
16945
      hasExponent = true;
16946
    }
16947
  }
16948
 
16949
  if (!hasExponent) {
16950
    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
16951
 
16952
    // determine fractionSize if it is not specified
16953
    if (isUndefined(fractionSize)) {
16954
      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
16955
    }
16956
 
16957
    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
16958
    // inspired by:
16959
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
16960
    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
16961
 
16962
    var fraction = ('' + number).split(DECIMAL_SEP);
16963
    var whole = fraction[0];
16964
    fraction = fraction[1] || '';
16965
 
16966
    var i, pos = 0,
16967
        lgroup = pattern.lgSize,
16968
        group = pattern.gSize;
16969
 
16970
    if (whole.length >= (lgroup + group)) {
16971
      pos = whole.length - lgroup;
16972
      for (i = 0; i < pos; i++) {
16973
        if ((pos - i) % group === 0 && i !== 0) {
16974
          formatedText += groupSep;
16975
        }
16976
        formatedText += whole.charAt(i);
16977
      }
16978
    }
16979
 
16980
    for (i = pos; i < whole.length; i++) {
16981
      if ((whole.length - i) % lgroup === 0 && i !== 0) {
16982
        formatedText += groupSep;
16983
      }
16984
      formatedText += whole.charAt(i);
16985
    }
16986
 
16987
    // format fraction part.
16988
    while (fraction.length < fractionSize) {
16989
      fraction += '0';
16990
    }
16991
 
16992
    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
16993
  } else {
16994
    if (fractionSize > 0 && number < 1) {
16995
      formatedText = number.toFixed(fractionSize);
16996
      number = parseFloat(formatedText);
16997
    }
16998
  }
16999
 
17000
  if (number === 0) {
17001
    isNegative = false;
17002
  }
17003
 
17004
  parts.push(isNegative ? pattern.negPre : pattern.posPre,
17005
             formatedText,
17006
             isNegative ? pattern.negSuf : pattern.posSuf);
17007
  return parts.join('');
17008
}
17009
 
17010
function padNumber(num, digits, trim) {
17011
  var neg = '';
17012
  if (num < 0) {
17013
    neg =  '-';
17014
    num = -num;
17015
  }
17016
  num = '' + num;
17017
  while (num.length < digits) num = '0' + num;
17018
  if (trim)
17019
    num = num.substr(num.length - digits);
17020
  return neg + num;
17021
}
17022
 
17023
 
17024
function dateGetter(name, size, offset, trim) {
17025
  offset = offset || 0;
17026
  return function(date) {
17027
    var value = date['get' + name]();
17028
    if (offset > 0 || value > -offset)
17029
      value += offset;
17030
    if (value === 0 && offset == -12) value = 12;
17031
    return padNumber(value, size, trim);
17032
  };
17033
}
17034
 
17035
function dateStrGetter(name, shortForm) {
17036
  return function(date, formats) {
17037
    var value = date['get' + name]();
17038
    var get = uppercase(shortForm ? ('SHORT' + name) : name);
17039
 
17040
    return formats[get][value];
17041
  };
17042
}
17043
 
17044
function timeZoneGetter(date) {
17045
  var zone = -1 * date.getTimezoneOffset();
17046
  var paddedZone = (zone >= 0) ? "+" : "";
17047
 
17048
  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
17049
                padNumber(Math.abs(zone % 60), 2);
17050
 
17051
  return paddedZone;
17052
}
17053
 
17054
function getFirstThursdayOfYear(year) {
17055
    // 0 = index of January
17056
    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
17057
    // 4 = index of Thursday (+1 to account for 1st = 5)
17058
    // 11 = index of *next* Thursday (+1 account for 1st = 12)
17059
    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
17060
}
17061
 
17062
function getThursdayThisWeek(datetime) {
17063
    return new Date(datetime.getFullYear(), datetime.getMonth(),
17064
      // 4 = index of Thursday
17065
      datetime.getDate() + (4 - datetime.getDay()));
17066
}
17067
 
17068
function weekGetter(size) {
17069
   return function(date) {
17070
      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
17071
         thisThurs = getThursdayThisWeek(date);
17072
 
17073
      var diff = +thisThurs - +firstThurs,
17074
         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
17075
 
17076
      return padNumber(result, size);
17077
   };
17078
}
17079
 
17080
function ampmGetter(date, formats) {
17081
  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
17082
}
17083
 
17084
var DATE_FORMATS = {
17085
  yyyy: dateGetter('FullYear', 4),
17086
    yy: dateGetter('FullYear', 2, 0, true),
17087
     y: dateGetter('FullYear', 1),
17088
  MMMM: dateStrGetter('Month'),
17089
   MMM: dateStrGetter('Month', true),
17090
    MM: dateGetter('Month', 2, 1),
17091
     M: dateGetter('Month', 1, 1),
17092
    dd: dateGetter('Date', 2),
17093
     d: dateGetter('Date', 1),
17094
    HH: dateGetter('Hours', 2),
17095
     H: dateGetter('Hours', 1),
17096
    hh: dateGetter('Hours', 2, -12),
17097
     h: dateGetter('Hours', 1, -12),
17098
    mm: dateGetter('Minutes', 2),
17099
     m: dateGetter('Minutes', 1),
17100
    ss: dateGetter('Seconds', 2),
17101
     s: dateGetter('Seconds', 1),
17102
     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
17103
     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
17104
   sss: dateGetter('Milliseconds', 3),
17105
  EEEE: dateStrGetter('Day'),
17106
   EEE: dateStrGetter('Day', true),
17107
     a: ampmGetter,
17108
     Z: timeZoneGetter,
17109
    ww: weekGetter(2),
17110
     w: weekGetter(1)
17111
};
17112
 
17113
var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
17114
    NUMBER_STRING = /^\-?\d+$/;
17115
 
17116
/**
17117
 * @ngdoc filter
17118
 * @name date
17119
 * @kind function
17120
 *
17121
 * @description
17122
 *   Formats `date` to a string based on the requested `format`.
17123
 *
17124
 *   `format` string can be composed of the following elements:
17125
 *
17126
 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
17127
 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
17128
 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
17129
 *   * `'MMMM'`: Month in year (January-December)
17130
 *   * `'MMM'`: Month in year (Jan-Dec)
17131
 *   * `'MM'`: Month in year, padded (01-12)
17132
 *   * `'M'`: Month in year (1-12)
17133
 *   * `'dd'`: Day in month, padded (01-31)
17134
 *   * `'d'`: Day in month (1-31)
17135
 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
17136
 *   * `'EEE'`: Day in Week, (Sun-Sat)
17137
 *   * `'HH'`: Hour in day, padded (00-23)
17138
 *   * `'H'`: Hour in day (0-23)
17139
 *   * `'hh'`: Hour in AM/PM, padded (01-12)
17140
 *   * `'h'`: Hour in AM/PM, (1-12)
17141
 *   * `'mm'`: Minute in hour, padded (00-59)
17142
 *   * `'m'`: Minute in hour (0-59)
17143
 *   * `'ss'`: Second in minute, padded (00-59)
17144
 *   * `'s'`: Second in minute (0-59)
17145
 *   * `'sss'`: Millisecond in second, padded (000-999)
17146
 *   * `'a'`: AM/PM marker
17147
 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
17148
 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
17149
 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
17150
 *
17151
 *   `format` string can also be one of the following predefined
17152
 *   {@link guide/i18n localizable formats}:
17153
 *
17154
 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
17155
 *     (e.g. Sep 3, 2010 12:05:08 PM)
17156
 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
17157
 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
17158
 *     (e.g. Friday, September 3, 2010)
17159
 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
17160
 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
17161
 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
17162
 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
17163
 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
17164
 *
17165
 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
17166
 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
17167
 *   (e.g. `"h 'o''clock'"`).
17168
 *
17169
 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
17170
 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
17171
 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
17172
 *    specified in the string input, the time is considered to be in the local timezone.
17173
 * @param {string=} format Formatting rules (see Description). If not specified,
17174
 *    `mediumDate` is used.
17175
 * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.
17176
 *    If not specified, the timezone of the browser will be used.
17177
 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
17178
 *
17179
 * @example
17180
   <example>
17181
     <file name="index.html">
17182
       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
17183
           <span>{{1288323623006 | date:'medium'}}</span><br>
17184
       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
17185
          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
17186
       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
17187
          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
17188
       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
17189
          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
17190
     </file>
17191
     <file name="protractor.js" type="protractor">
17192
       it('should format date', function() {
17193
         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
17194
            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
17195
         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
17196
            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
17197
         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
17198
            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
17199
         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
17200
            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
17201
       });
17202
     </file>
17203
   </example>
17204
 */
17205
dateFilter.$inject = ['$locale'];
17206
function dateFilter($locale) {
17207
 
17208
 
17209
  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
17210
                     // 1        2       3         4          5          6          7          8  9     10      11
17211
  function jsonStringToDate(string) {
17212
    var match;
17213
    if (match = string.match(R_ISO8601_STR)) {
17214
      var date = new Date(0),
17215
          tzHour = 0,
17216
          tzMin  = 0,
17217
          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
17218
          timeSetter = match[8] ? date.setUTCHours : date.setHours;
17219
 
17220
      if (match[9]) {
17221
        tzHour = int(match[9] + match[10]);
17222
        tzMin = int(match[9] + match[11]);
17223
      }
17224
      dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17225
      var h = int(match[4] || 0) - tzHour;
17226
      var m = int(match[5] || 0) - tzMin;
17227
      var s = int(match[6] || 0);
17228
      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17229
      timeSetter.call(date, h, m, s, ms);
17230
      return date;
17231
    }
17232
    return string;
17233
  }
17234
 
17235
 
17236
  return function(date, format, timezone) {
17237
    var text = '',
17238
        parts = [],
17239
        fn, match;
17240
 
17241
    format = format || 'mediumDate';
17242
    format = $locale.DATETIME_FORMATS[format] || format;
17243
    if (isString(date)) {
17244
      date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
17245
    }
17246
 
17247
    if (isNumber(date)) {
17248
      date = new Date(date);
17249
    }
17250
 
17251
    if (!isDate(date)) {
17252
      return date;
17253
    }
17254
 
17255
    while (format) {
17256
      match = DATE_FORMATS_SPLIT.exec(format);
17257
      if (match) {
17258
        parts = concat(parts, match, 1);
17259
        format = parts.pop();
17260
      } else {
17261
        parts.push(format);
17262
        format = null;
17263
      }
17264
    }
17265
 
17266
    if (timezone && timezone === 'UTC') {
17267
      date = new Date(date.getTime());
17268
      date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
17269
    }
17270
    forEach(parts, function(value) {
17271
      fn = DATE_FORMATS[value];
17272
      text += fn ? fn(date, $locale.DATETIME_FORMATS)
17273
                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
17274
    });
17275
 
17276
    return text;
17277
  };
17278
}
17279
 
17280
 
17281
/**
17282
 * @ngdoc filter
17283
 * @name json
17284
 * @kind function
17285
 *
17286
 * @description
17287
 *   Allows you to convert a JavaScript object into JSON string.
17288
 *
17289
 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
17290
 *   the binding is automatically converted to JSON.
17291
 *
17292
 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
17293
 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
17294
 * @returns {string} JSON string.
17295
 *
17296
 *
17297
 * @example
17298
   <example>
17299
     <file name="index.html">
17300
       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
17301
       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
17302
     </file>
17303
     <file name="protractor.js" type="protractor">
17304
       it('should jsonify filtered objects', function() {
17305
         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
17306
         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
17307
       });
17308
     </file>
17309
   </example>
17310
 *
17311
 */
17312
function jsonFilter() {
17313
  return function(object, spacing) {
17314
    if (isUndefined(spacing)) {
17315
        spacing = 2;
17316
    }
17317
    return toJson(object, spacing);
17318
  };
17319
}
17320
 
17321
 
17322
/**
17323
 * @ngdoc filter
17324
 * @name lowercase
17325
 * @kind function
17326
 * @description
17327
 * Converts string to lowercase.
17328
 * @see angular.lowercase
17329
 */
17330
var lowercaseFilter = valueFn(lowercase);
17331
 
17332
 
17333
/**
17334
 * @ngdoc filter
17335
 * @name uppercase
17336
 * @kind function
17337
 * @description
17338
 * Converts string to uppercase.
17339
 * @see angular.uppercase
17340
 */
17341
var uppercaseFilter = valueFn(uppercase);
17342
 
17343
/**
17344
 * @ngdoc filter
17345
 * @name limitTo
17346
 * @kind function
17347
 *
17348
 * @description
17349
 * Creates a new array or string containing only a specified number of elements. The elements
17350
 * are taken from either the beginning or the end of the source array, string or number, as specified by
17351
 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
17352
 * converted to a string.
17353
 *
17354
 * @param {Array|string|number} input Source array, string or number to be limited.
17355
 * @param {string|number} limit The length of the returned array or string. If the `limit` number
17356
 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
17357
 *     If the number is negative, `limit` number  of items from the end of the source array/string
17358
 *     are copied. The `limit` will be trimmed if it exceeds `array.length`
17359
 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
17360
 *     had less than `limit` elements.
17361
 *
17362
 * @example
17363
   <example module="limitToExample">
17364
     <file name="index.html">
17365
       <script>
17366
         angular.module('limitToExample', [])
17367
           .controller('ExampleController', ['$scope', function($scope) {
17368
             $scope.numbers = [1,2,3,4,5,6,7,8,9];
17369
             $scope.letters = "abcdefghi";
17370
             $scope.longNumber = 2345432342;
17371
             $scope.numLimit = 3;
17372
             $scope.letterLimit = 3;
17373
             $scope.longNumberLimit = 3;
17374
           }]);
17375
       </script>
17376
       <div ng-controller="ExampleController">
17377
         Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
17378
         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
17379
         Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
17380
         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
17381
         Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit">
17382
         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
17383
       </div>
17384
     </file>
17385
     <file name="protractor.js" type="protractor">
17386
       var numLimitInput = element(by.model('numLimit'));
17387
       var letterLimitInput = element(by.model('letterLimit'));
17388
       var longNumberLimitInput = element(by.model('longNumberLimit'));
17389
       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
17390
       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
17391
       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
17392
 
17393
       it('should limit the number array to first three items', function() {
17394
         expect(numLimitInput.getAttribute('value')).toBe('3');
17395
         expect(letterLimitInput.getAttribute('value')).toBe('3');
17396
         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
17397
         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
17398
         expect(limitedLetters.getText()).toEqual('Output letters: abc');
17399
         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
17400
       });
17401
 
17402
       // There is a bug in safari and protractor that doesn't like the minus key
17403
       // it('should update the output when -3 is entered', function() {
17404
       //   numLimitInput.clear();
17405
       //   numLimitInput.sendKeys('-3');
17406
       //   letterLimitInput.clear();
17407
       //   letterLimitInput.sendKeys('-3');
17408
       //   longNumberLimitInput.clear();
17409
       //   longNumberLimitInput.sendKeys('-3');
17410
       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
17411
       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
17412
       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
17413
       // });
17414
 
17415
       it('should not exceed the maximum size of input array', function() {
17416
         numLimitInput.clear();
17417
         numLimitInput.sendKeys('100');
17418
         letterLimitInput.clear();
17419
         letterLimitInput.sendKeys('100');
17420
         longNumberLimitInput.clear();
17421
         longNumberLimitInput.sendKeys('100');
17422
         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
17423
         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
17424
         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
17425
       });
17426
     </file>
17427
   </example>
17428
*/
17429
function limitToFilter() {
17430
  return function(input, limit) {
17431
    if (isNumber(input)) input = input.toString();
17432
    if (!isArray(input) && !isString(input)) return input;
17433
 
17434
    if (Math.abs(Number(limit)) === Infinity) {
17435
      limit = Number(limit);
17436
    } else {
17437
      limit = int(limit);
17438
    }
17439
 
17440
    //NaN check on limit
17441
    if (limit) {
17442
      return limit > 0 ? input.slice(0, limit) : input.slice(limit);
17443
    } else {
17444
      return isString(input) ? "" : [];
17445
    }
17446
  };
17447
}
17448
 
17449
/**
17450
 * @ngdoc filter
17451
 * @name orderBy
17452
 * @kind function
17453
 *
17454
 * @description
17455
 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
17456
 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
17457
 * correctly, make sure they are actually being saved as numbers and not strings.
17458
 *
17459
 * @param {Array} array The array to sort.
17460
 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
17461
 *    used by the comparator to determine the order of elements.
17462
 *
17463
 *    Can be one of:
17464
 *
17465
 *    - `function`: Getter function. The result of this function will be sorted using the
17466
 *      `<`, `=`, `>` operator.
17467
 *    - `string`: An Angular expression. The result of this expression is used to compare elements
17468
 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
17469
 *      3 first characters of a property called `name`). The result of a constant expression
17470
 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
17471
 *      to sort object by the value of their `special name` property). An expression can be
17472
 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
17473
 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
17474
 *      element itself is used to compare where sorting.
17475
 *    - `Array`: An array of function or string predicates. The first predicate in the array
17476
 *      is used for sorting, but when two items are equivalent, the next predicate is used.
17477
 *
17478
 *    If the predicate is missing or empty then it defaults to `'+'`.
17479
 *
17480
 * @param {boolean=} reverse Reverse the order of the array.
17481
 * @returns {Array} Sorted copy of the source array.
17482
 *
17483
 * @example
17484
   <example module="orderByExample">
17485
     <file name="index.html">
17486
       <script>
17487
         angular.module('orderByExample', [])
17488
           .controller('ExampleController', ['$scope', function($scope) {
17489
             $scope.friends =
17490
                 [{name:'John', phone:'555-1212', age:10},
17491
                  {name:'Mary', phone:'555-9876', age:19},
17492
                  {name:'Mike', phone:'555-4321', age:21},
17493
                  {name:'Adam', phone:'555-5678', age:35},
17494
                  {name:'Julie', phone:'555-8765', age:29}];
17495
             $scope.predicate = '-age';
17496
           }]);
17497
       </script>
17498
       <div ng-controller="ExampleController">
17499
         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
17500
         <hr/>
17501
         [ <a href="" ng-click="predicate=''">unsorted</a> ]
17502
         <table class="friend">
17503
           <tr>
17504
             <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
17505
                 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
17506
             <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
17507
             <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
17508
           </tr>
17509
           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
17510
             <td>{{friend.name}}</td>
17511
             <td>{{friend.phone}}</td>
17512
             <td>{{friend.age}}</td>
17513
           </tr>
17514
         </table>
17515
       </div>
17516
     </file>
17517
   </example>
17518
 *
17519
 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
17520
 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
17521
 * desired parameters.
17522
 *
17523
 * Example:
17524
 *
17525
 * @example
17526
  <example module="orderByExample">
17527
    <file name="index.html">
17528
      <div ng-controller="ExampleController">
17529
        <table class="friend">
17530
          <tr>
17531
            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
17532
              (<a href="" ng-click="order('-name',false)">^</a>)</th>
17533
            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
17534
            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
17535
          </tr>
17536
          <tr ng-repeat="friend in friends">
17537
            <td>{{friend.name}}</td>
17538
            <td>{{friend.phone}}</td>
17539
            <td>{{friend.age}}</td>
17540
          </tr>
17541
        </table>
17542
      </div>
17543
    </file>
17544
 
17545
    <file name="script.js">
17546
      angular.module('orderByExample', [])
17547
        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
17548
          var orderBy = $filter('orderBy');
17549
          $scope.friends = [
17550
            { name: 'John',    phone: '555-1212',    age: 10 },
17551
            { name: 'Mary',    phone: '555-9876',    age: 19 },
17552
            { name: 'Mike',    phone: '555-4321',    age: 21 },
17553
            { name: 'Adam',    phone: '555-5678',    age: 35 },
17554
            { name: 'Julie',   phone: '555-8765',    age: 29 }
17555
          ];
17556
          $scope.order = function(predicate, reverse) {
17557
            $scope.friends = orderBy($scope.friends, predicate, reverse);
17558
          };
17559
          $scope.order('-age',false);
17560
        }]);
17561
    </file>
17562
</example>
17563
 */
17564
orderByFilter.$inject = ['$parse'];
17565
function orderByFilter($parse) {
17566
  return function(array, sortPredicate, reverseOrder) {
17567
    if (!(isArrayLike(array))) return array;
17568
    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17569
    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17570
    sortPredicate = sortPredicate.map(function(predicate) {
17571
      var descending = false, get = predicate || identity;
17572
      if (isString(predicate)) {
17573
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
17574
          descending = predicate.charAt(0) == '-';
17575
          predicate = predicate.substring(1);
17576
        }
17577
        if (predicate === '') {
17578
          // Effectively no predicate was passed so we compare identity
17579
          return reverseComparator(compare, descending);
17580
        }
17581
        get = $parse(predicate);
17582
        if (get.constant) {
17583
          var key = get();
17584
          return reverseComparator(function(a, b) {
17585
            return compare(a[key], b[key]);
17586
          }, descending);
17587
        }
17588
      }
17589
      return reverseComparator(function(a, b) {
17590
        return compare(get(a),get(b));
17591
      }, descending);
17592
    });
17593
    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17594
 
17595
    function comparator(o1, o2) {
17596
      for (var i = 0; i < sortPredicate.length; i++) {
17597
        var comp = sortPredicate[i](o1, o2);
17598
        if (comp !== 0) return comp;
17599
      }
17600
      return 0;
17601
    }
17602
    function reverseComparator(comp, descending) {
17603
      return descending
17604
          ? function(a, b) {return comp(b,a);}
17605
          : comp;
17606
    }
17607
 
17608
    function isPrimitive(value) {
17609
      switch (typeof value) {
17610
        case 'number': /* falls through */
17611
        case 'boolean': /* falls through */
17612
        case 'string':
17613
          return true;
17614
        default:
17615
          return false;
17616
      }
17617
    }
17618
 
17619
    function objectToString(value) {
17620
      if (value === null) return 'null';
17621
      if (typeof value.valueOf === 'function') {
17622
        value = value.valueOf();
17623
        if (isPrimitive(value)) return value;
17624
      }
17625
      if (typeof value.toString === 'function') {
17626
        value = value.toString();
17627
        if (isPrimitive(value)) return value;
17628
      }
17629
      return '';
17630
    }
17631
 
17632
    function compare(v1, v2) {
17633
      var t1 = typeof v1;
17634
      var t2 = typeof v2;
17635
      if (t1 === t2 && t1 === "object") {
17636
        v1 = objectToString(v1);
17637
        v2 = objectToString(v2);
17638
      }
17639
      if (t1 === t2) {
17640
        if (t1 === "string") {
17641
           v1 = v1.toLowerCase();
17642
           v2 = v2.toLowerCase();
17643
        }
17644
        if (v1 === v2) return 0;
17645
        return v1 < v2 ? -1 : 1;
17646
      } else {
17647
        return t1 < t2 ? -1 : 1;
17648
      }
17649
    }
17650
  };
17651
}
17652
 
17653
function ngDirective(directive) {
17654
  if (isFunction(directive)) {
17655
    directive = {
17656
      link: directive
17657
    };
17658
  }
17659
  directive.restrict = directive.restrict || 'AC';
17660
  return valueFn(directive);
17661
}
17662
 
17663
/**
17664
 * @ngdoc directive
17665
 * @name a
17666
 * @restrict E
17667
 *
17668
 * @description
17669
 * Modifies the default behavior of the html A tag so that the default action is prevented when
17670
 * the href attribute is empty.
17671
 *
17672
 * This change permits the easy creation of action links with the `ngClick` directive
17673
 * without changing the location or causing page reloads, e.g.:
17674
 * `<a href="" ng-click="list.addItem()">Add Item</a>`
17675
 */
17676
var htmlAnchorDirective = valueFn({
17677
  restrict: 'E',
17678
  compile: function(element, attr) {
17679
    if (!attr.href && !attr.xlinkHref && !attr.name) {
17680
      return function(scope, element) {
17681
        // If the linked element is not an anchor tag anymore, do nothing
17682
        if (element[0].nodeName.toLowerCase() !== 'a') return;
17683
 
17684
        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
17685
        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
17686
                   'xlink:href' : 'href';
17687
        element.on('click', function(event) {
17688
          // if we have no href url, then don't navigate anywhere.
17689
          if (!element.attr(href)) {
17690
            event.preventDefault();
17691
          }
17692
        });
17693
      };
17694
    }
17695
  }
17696
});
17697
 
17698
/**
17699
 * @ngdoc directive
17700
 * @name ngHref
17701
 * @restrict A
17702
 * @priority 99
17703
 *
17704
 * @description
17705
 * Using Angular markup like `{{hash}}` in an href attribute will
17706
 * make the link go to the wrong URL if the user clicks it before
17707
 * Angular has a chance to replace the `{{hash}}` markup with its
17708
 * value. Until Angular replaces the markup the link will be broken
17709
 * and will most likely return a 404 error. The `ngHref` directive
17710
 * solves this problem.
17711
 *
17712
 * The wrong way to write it:
17713
 * ```html
17714
 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17715
 * ```
17716
 *
17717
 * The correct way to write it:
17718
 * ```html
17719
 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17720
 * ```
17721
 *
17722
 * @element A
17723
 * @param {template} ngHref any string which can contain `{{}}` markup.
17724
 *
17725
 * @example
17726
 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
17727
 * in links and their different behaviors:
17728
    <example>
17729
      <file name="index.html">
17730
        <input ng-model="value" /><br />
17731
        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
17732
        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
17733
        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
17734
        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
17735
        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
17736
        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
17737
      </file>
17738
      <file name="protractor.js" type="protractor">
17739
        it('should execute ng-click but not reload when href without value', function() {
17740
          element(by.id('link-1')).click();
17741
          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
17742
          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
17743
        });
17744
 
17745
        it('should execute ng-click but not reload when href empty string', function() {
17746
          element(by.id('link-2')).click();
17747
          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
17748
          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
17749
        });
17750
 
17751
        it('should execute ng-click and change url when ng-href specified', function() {
17752
          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
17753
 
17754
          element(by.id('link-3')).click();
17755
 
17756
          // At this point, we navigate away from an Angular page, so we need
17757
          // to use browser.driver to get the base webdriver.
17758
 
17759
          browser.wait(function() {
17760
            return browser.driver.getCurrentUrl().then(function(url) {
17761
              return url.match(/\/123$/);
17762
            });
17763
          }, 5000, 'page should navigate to /123');
17764
        });
17765
 
17766
        xit('should execute ng-click but not reload when href empty string and name specified', function() {
17767
          element(by.id('link-4')).click();
17768
          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
17769
          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
17770
        });
17771
 
17772
        it('should execute ng-click but not reload when no href but name specified', function() {
17773
          element(by.id('link-5')).click();
17774
          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
17775
          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
17776
        });
17777
 
17778
        it('should only change url when only ng-href', function() {
17779
          element(by.model('value')).clear();
17780
          element(by.model('value')).sendKeys('6');
17781
          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
17782
 
17783
          element(by.id('link-6')).click();
17784
 
17785
          // At this point, we navigate away from an Angular page, so we need
17786
          // to use browser.driver to get the base webdriver.
17787
          browser.wait(function() {
17788
            return browser.driver.getCurrentUrl().then(function(url) {
17789
              return url.match(/\/6$/);
17790
            });
17791
          }, 5000, 'page should navigate to /6');
17792
        });
17793
      </file>
17794
    </example>
17795
 */
17796
 
17797
/**
17798
 * @ngdoc directive
17799
 * @name ngSrc
17800
 * @restrict A
17801
 * @priority 99
17802
 *
17803
 * @description
17804
 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
17805
 * work right: The browser will fetch from the URL with the literal
17806
 * text `{{hash}}` until Angular replaces the expression inside
17807
 * `{{hash}}`. The `ngSrc` directive solves this problem.
17808
 *
17809
 * The buggy way to write it:
17810
 * ```html
17811
 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
17812
 * ```
17813
 *
17814
 * The correct way to write it:
17815
 * ```html
17816
 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
17817
 * ```
17818
 *
17819
 * @element IMG
17820
 * @param {template} ngSrc any string which can contain `{{}}` markup.
17821
 */
17822
 
17823
/**
17824
 * @ngdoc directive
17825
 * @name ngSrcset
17826
 * @restrict A
17827
 * @priority 99
17828
 *
17829
 * @description
17830
 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
17831
 * work right: The browser will fetch from the URL with the literal
17832
 * text `{{hash}}` until Angular replaces the expression inside
17833
 * `{{hash}}`. The `ngSrcset` directive solves this problem.
17834
 *
17835
 * The buggy way to write it:
17836
 * ```html
17837
 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17838
 * ```
17839
 *
17840
 * The correct way to write it:
17841
 * ```html
17842
 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17843
 * ```
17844
 *
17845
 * @element IMG
17846
 * @param {template} ngSrcset any string which can contain `{{}}` markup.
17847
 */
17848
 
17849
/**
17850
 * @ngdoc directive
17851
 * @name ngDisabled
17852
 * @restrict A
17853
 * @priority 100
17854
 *
17855
 * @description
17856
 *
17857
 * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
17858
 * ```html
17859
 * <div ng-init="scope = { isDisabled: false }">
17860
 *  <button disabled="{{scope.isDisabled}}">Disabled</button>
17861
 * </div>
17862
 * ```
17863
 *
17864
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17865
 * such as disabled. (Their presence means true and their absence means false.)
17866
 * If we put an Angular interpolation expression into such an attribute then the
17867
 * binding information would be lost when the browser removes the attribute.
17868
 * The `ngDisabled` directive solves this problem for the `disabled` attribute.
17869
 * This complementary directive is not removed by the browser and so provides
17870
 * a permanent reliable place to store the binding information.
17871
 *
17872
 * @example
17873
    <example>
17874
      <file name="index.html">
17875
        Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
17876
        <button ng-model="button" ng-disabled="checked">Button</button>
17877
      </file>
17878
      <file name="protractor.js" type="protractor">
17879
        it('should toggle button', function() {
17880
          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
17881
          element(by.model('checked')).click();
17882
          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
17883
        });
17884
      </file>
17885
    </example>
17886
 *
17887
 * @element INPUT
17888
 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
17889
 *     then special attribute "disabled" will be set on the element
17890
 */
17891
 
17892
 
17893
/**
17894
 * @ngdoc directive
17895
 * @name ngChecked
17896
 * @restrict A
17897
 * @priority 100
17898
 *
17899
 * @description
17900
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17901
 * such as checked. (Their presence means true and their absence means false.)
17902
 * If we put an Angular interpolation expression into such an attribute then the
17903
 * binding information would be lost when the browser removes the attribute.
17904
 * The `ngChecked` directive solves this problem for the `checked` attribute.
17905
 * This complementary directive is not removed by the browser and so provides
17906
 * a permanent reliable place to store the binding information.
17907
 * @example
17908
    <example>
17909
      <file name="index.html">
17910
        Check me to check both: <input type="checkbox" ng-model="master"><br/>
17911
        <input id="checkSlave" type="checkbox" ng-checked="master">
17912
      </file>
17913
      <file name="protractor.js" type="protractor">
17914
        it('should check both checkBoxes', function() {
17915
          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
17916
          element(by.model('master')).click();
17917
          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
17918
        });
17919
      </file>
17920
    </example>
17921
 *
17922
 * @element INPUT
17923
 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
17924
 *     then special attribute "checked" will be set on the element
17925
 */
17926
 
17927
 
17928
/**
17929
 * @ngdoc directive
17930
 * @name ngReadonly
17931
 * @restrict A
17932
 * @priority 100
17933
 *
17934
 * @description
17935
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17936
 * such as readonly. (Their presence means true and their absence means false.)
17937
 * If we put an Angular interpolation expression into such an attribute then the
17938
 * binding information would be lost when the browser removes the attribute.
17939
 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
17940
 * This complementary directive is not removed by the browser and so provides
17941
 * a permanent reliable place to store the binding information.
17942
 * @example
17943
    <example>
17944
      <file name="index.html">
17945
        Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
17946
        <input type="text" ng-readonly="checked" value="I'm Angular"/>
17947
      </file>
17948
      <file name="protractor.js" type="protractor">
17949
        it('should toggle readonly attr', function() {
17950
          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
17951
          element(by.model('checked')).click();
17952
          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
17953
        });
17954
      </file>
17955
    </example>
17956
 *
17957
 * @element INPUT
17958
 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
17959
 *     then special attribute "readonly" will be set on the element
17960
 */
17961
 
17962
 
17963
/**
17964
 * @ngdoc directive
17965
 * @name ngSelected
17966
 * @restrict A
17967
 * @priority 100
17968
 *
17969
 * @description
17970
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17971
 * such as selected. (Their presence means true and their absence means false.)
17972
 * If we put an Angular interpolation expression into such an attribute then the
17973
 * binding information would be lost when the browser removes the attribute.
17974
 * The `ngSelected` directive solves this problem for the `selected` attribute.
17975
 * This complementary directive is not removed by the browser and so provides
17976
 * a permanent reliable place to store the binding information.
17977
 *
17978
 * @example
17979
    <example>
17980
      <file name="index.html">
17981
        Check me to select: <input type="checkbox" ng-model="selected"><br/>
17982
        <select>
17983
          <option>Hello!</option>
17984
          <option id="greet" ng-selected="selected">Greetings!</option>
17985
        </select>
17986
      </file>
17987
      <file name="protractor.js" type="protractor">
17988
        it('should select Greetings!', function() {
17989
          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
17990
          element(by.model('selected')).click();
17991
          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
17992
        });
17993
      </file>
17994
    </example>
17995
 *
17996
 * @element OPTION
17997
 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
17998
 *     then special attribute "selected" will be set on the element
17999
 */
18000
 
18001
/**
18002
 * @ngdoc directive
18003
 * @name ngOpen
18004
 * @restrict A
18005
 * @priority 100
18006
 *
18007
 * @description
18008
 * The HTML specification does not require browsers to preserve the values of boolean attributes
18009
 * such as open. (Their presence means true and their absence means false.)
18010
 * If we put an Angular interpolation expression into such an attribute then the
18011
 * binding information would be lost when the browser removes the attribute.
18012
 * The `ngOpen` directive solves this problem for the `open` attribute.
18013
 * This complementary directive is not removed by the browser and so provides
18014
 * a permanent reliable place to store the binding information.
18015
 * @example
18016
     <example>
18017
       <file name="index.html">
18018
         Check me check multiple: <input type="checkbox" ng-model="open"><br/>
18019
         <details id="details" ng-open="open">
18020
            <summary>Show/Hide me</summary>
18021
         </details>
18022
       </file>
18023
       <file name="protractor.js" type="protractor">
18024
         it('should toggle open', function() {
18025
           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
18026
           element(by.model('open')).click();
18027
           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
18028
         });
18029
       </file>
18030
     </example>
18031
 *
18032
 * @element DETAILS
18033
 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
18034
 *     then special attribute "open" will be set on the element
18035
 */
18036
 
18037
var ngAttributeAliasDirectives = {};
18038
 
18039
 
18040
// boolean attrs are evaluated
18041
forEach(BOOLEAN_ATTR, function(propName, attrName) {
18042
  // binding to multiple is not supported
18043
  if (propName == "multiple") return;
18044
 
18045
  var normalized = directiveNormalize('ng-' + attrName);
18046
  ngAttributeAliasDirectives[normalized] = function() {
18047
    return {
18048
      restrict: 'A',
18049
      priority: 100,
18050
      link: function(scope, element, attr) {
18051
        scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
18052
          attr.$set(attrName, !!value);
18053
        });
18054
      }
18055
    };
18056
  };
18057
});
18058
 
18059
// aliased input attrs are evaluated
18060
forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
18061
  ngAttributeAliasDirectives[ngAttr] = function() {
18062
    return {
18063
      priority: 100,
18064
      link: function(scope, element, attr) {
18065
        //special case ngPattern when a literal regular expression value
18066
        //is used as the expression (this way we don't have to watch anything).
18067
        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
18068
          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
18069
          if (match) {
18070
            attr.$set("ngPattern", new RegExp(match[1], match[2]));
18071
            return;
18072
          }
18073
        }
18074
 
18075
        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
18076
          attr.$set(ngAttr, value);
18077
        });
18078
      }
18079
    };
18080
  };
18081
});
18082
 
18083
// ng-src, ng-srcset, ng-href are interpolated
18084
forEach(['src', 'srcset', 'href'], function(attrName) {
18085
  var normalized = directiveNormalize('ng-' + attrName);
18086
  ngAttributeAliasDirectives[normalized] = function() {
18087
    return {
18088
      priority: 99, // it needs to run after the attributes are interpolated
18089
      link: function(scope, element, attr) {
18090
        var propName = attrName,
18091
            name = attrName;
18092
 
18093
        if (attrName === 'href' &&
18094
            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
18095
          name = 'xlinkHref';
18096
          attr.$attr[name] = 'xlink:href';
18097
          propName = null;
18098
        }
18099
 
18100
        attr.$observe(normalized, function(value) {
18101
          if (!value) {
18102
            if (attrName === 'href') {
18103
              attr.$set(name, null);
18104
            }
18105
            return;
18106
          }
18107
 
18108
          attr.$set(name, value);
18109
 
18110
          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
18111
          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
18112
          // to set the property as well to achieve the desired effect.
18113
          // we use attr[attrName] value since $set can sanitize the url.
18114
          if (msie && propName) element.prop(propName, attr[name]);
18115
        });
18116
      }
18117
    };
18118
  };
18119
});
18120
 
18121
/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
18122
 */
18123
var nullFormCtrl = {
18124
  $addControl: noop,
18125
  $$renameControl: nullFormRenameControl,
18126
  $removeControl: noop,
18127
  $setValidity: noop,
18128
  $setDirty: noop,
18129
  $setPristine: noop,
18130
  $setSubmitted: noop
18131
},
18132
SUBMITTED_CLASS = 'ng-submitted';
18133
 
18134
function nullFormRenameControl(control, name) {
18135
  control.$name = name;
18136
}
18137
 
18138
/**
18139
 * @ngdoc type
18140
 * @name form.FormController
18141
 *
18142
 * @property {boolean} $pristine True if user has not interacted with the form yet.
18143
 * @property {boolean} $dirty True if user has already interacted with the form.
18144
 * @property {boolean} $valid True if all of the containing forms and controls are valid.
18145
 * @property {boolean} $invalid True if at least one containing control or form is invalid.
18146
 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
18147
 *
18148
 * @property {Object} $error Is an object hash, containing references to controls or
18149
 *  forms with failing validators, where:
18150
 *
18151
 *  - keys are validation tokens (error names),
18152
 *  - values are arrays of controls or forms that have a failing validator for given error name.
18153
 *
18154
 *  Built-in validation tokens:
18155
 *
18156
 *  - `email`
18157
 *  - `max`
18158
 *  - `maxlength`
18159
 *  - `min`
18160
 *  - `minlength`
18161
 *  - `number`
18162
 *  - `pattern`
18163
 *  - `required`
18164
 *  - `url`
18165
 *  - `date`
18166
 *  - `datetimelocal`
18167
 *  - `time`
18168
 *  - `week`
18169
 *  - `month`
18170
 *
18171
 * @description
18172
 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
18173
 * such as being valid/invalid or dirty/pristine.
18174
 *
18175
 * Each {@link ng.directive:form form} directive creates an instance
18176
 * of `FormController`.
18177
 *
18178
 */
18179
//asks for $scope to fool the BC controller module
18180
FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
18181
function FormController(element, attrs, $scope, $animate, $interpolate) {
18182
  var form = this,
18183
      controls = [];
18184
 
18185
  var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
18186
 
18187
  // init state
18188
  form.$error = {};
18189
  form.$$success = {};
18190
  form.$pending = undefined;
18191
  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
18192
  form.$dirty = false;
18193
  form.$pristine = true;
18194
  form.$valid = true;
18195
  form.$invalid = false;
18196
  form.$submitted = false;
18197
 
18198
  parentForm.$addControl(form);
18199
 
18200
  /**
18201
   * @ngdoc method
18202
   * @name form.FormController#$rollbackViewValue
18203
   *
18204
   * @description
18205
   * Rollback all form controls pending updates to the `$modelValue`.
18206
   *
18207
   * Updates may be pending by a debounced event or because the input is waiting for a some future
18208
   * event defined in `ng-model-options`. This method is typically needed by the reset button of
18209
   * a form that uses `ng-model-options` to pend updates.
18210
   */
18211
  form.$rollbackViewValue = function() {
18212
    forEach(controls, function(control) {
18213
      control.$rollbackViewValue();
18214
    });
18215
  };
18216
 
18217
  /**
18218
   * @ngdoc method
18219
   * @name form.FormController#$commitViewValue
18220
   *
18221
   * @description
18222
   * Commit all form controls pending updates to the `$modelValue`.
18223
   *
18224
   * Updates may be pending by a debounced event or because the input is waiting for a some future
18225
   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
18226
   * usually handles calling this in response to input events.
18227
   */
18228
  form.$commitViewValue = function() {
18229
    forEach(controls, function(control) {
18230
      control.$commitViewValue();
18231
    });
18232
  };
18233
 
18234
  /**
18235
   * @ngdoc method
18236
   * @name form.FormController#$addControl
18237
   *
18238
   * @description
18239
   * Register a control with the form.
18240
   *
18241
   * Input elements using ngModelController do this automatically when they are linked.
18242
   */
18243
  form.$addControl = function(control) {
18244
    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
18245
    // and not added to the scope.  Now we throw an error.
18246
    assertNotHasOwnProperty(control.$name, 'input');
18247
    controls.push(control);
18248
 
18249
    if (control.$name) {
18250
      form[control.$name] = control;
18251
    }
18252
  };
18253
 
18254
  // Private API: rename a form control
18255
  form.$$renameControl = function(control, newName) {
18256
    var oldName = control.$name;
18257
 
18258
    if (form[oldName] === control) {
18259
      delete form[oldName];
18260
    }
18261
    form[newName] = control;
18262
    control.$name = newName;
18263
  };
18264
 
18265
  /**
18266
   * @ngdoc method
18267
   * @name form.FormController#$removeControl
18268
   *
18269
   * @description
18270
   * Deregister a control from the form.
18271
   *
18272
   * Input elements using ngModelController do this automatically when they are destroyed.
18273
   */
18274
  form.$removeControl = function(control) {
18275
    if (control.$name && form[control.$name] === control) {
18276
      delete form[control.$name];
18277
    }
18278
    forEach(form.$pending, function(value, name) {
18279
      form.$setValidity(name, null, control);
18280
    });
18281
    forEach(form.$error, function(value, name) {
18282
      form.$setValidity(name, null, control);
18283
    });
18284
    forEach(form.$$success, function(value, name) {
18285
      form.$setValidity(name, null, control);
18286
    });
18287
 
18288
    arrayRemove(controls, control);
18289
  };
18290
 
18291
 
18292
  /**
18293
   * @ngdoc method
18294
   * @name form.FormController#$setValidity
18295
   *
18296
   * @description
18297
   * Sets the validity of a form control.
18298
   *
18299
   * This method will also propagate to parent forms.
18300
   */
18301
  addSetValidityMethod({
18302
    ctrl: this,
18303
    $element: element,
18304
    set: function(object, property, controller) {
18305
      var list = object[property];
18306
      if (!list) {
18307
        object[property] = [controller];
18308
      } else {
18309
        var index = list.indexOf(controller);
18310
        if (index === -1) {
18311
          list.push(controller);
18312
        }
18313
      }
18314
    },
18315
    unset: function(object, property, controller) {
18316
      var list = object[property];
18317
      if (!list) {
18318
        return;
18319
      }
18320
      arrayRemove(list, controller);
18321
      if (list.length === 0) {
18322
        delete object[property];
18323
      }
18324
    },
18325
    parentForm: parentForm,
18326
    $animate: $animate
18327
  });
18328
 
18329
  /**
18330
   * @ngdoc method
18331
   * @name form.FormController#$setDirty
18332
   *
18333
   * @description
18334
   * Sets the form to a dirty state.
18335
   *
18336
   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
18337
   * state (ng-dirty class). This method will also propagate to parent forms.
18338
   */
18339
  form.$setDirty = function() {
18340
    $animate.removeClass(element, PRISTINE_CLASS);
18341
    $animate.addClass(element, DIRTY_CLASS);
18342
    form.$dirty = true;
18343
    form.$pristine = false;
18344
    parentForm.$setDirty();
18345
  };
18346
 
18347
  /**
18348
   * @ngdoc method
18349
   * @name form.FormController#$setPristine
18350
   *
18351
   * @description
18352
   * Sets the form to its pristine state.
18353
   *
18354
   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
18355
   * state (ng-pristine class). This method will also propagate to all the controls contained
18356
   * in this form.
18357
   *
18358
   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
18359
   * saving or resetting it.
18360
   */
18361
  form.$setPristine = function() {
18362
    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
18363
    form.$dirty = false;
18364
    form.$pristine = true;
18365
    form.$submitted = false;
18366
    forEach(controls, function(control) {
18367
      control.$setPristine();
18368
    });
18369
  };
18370
 
18371
  /**
18372
   * @ngdoc method
18373
   * @name form.FormController#$setUntouched
18374
   *
18375
   * @description
18376
   * Sets the form to its untouched state.
18377
   *
18378
   * This method can be called to remove the 'ng-touched' class and set the form controls to their
18379
   * untouched state (ng-untouched class).
18380
   *
18381
   * Setting a form controls back to their untouched state is often useful when setting the form
18382
   * back to its pristine state.
18383
   */
18384
  form.$setUntouched = function() {
18385
    forEach(controls, function(control) {
18386
      control.$setUntouched();
18387
    });
18388
  };
18389
 
18390
  /**
18391
   * @ngdoc method
18392
   * @name form.FormController#$setSubmitted
18393
   *
18394
   * @description
18395
   * Sets the form to its submitted state.
18396
   */
18397
  form.$setSubmitted = function() {
18398
    $animate.addClass(element, SUBMITTED_CLASS);
18399
    form.$submitted = true;
18400
    parentForm.$setSubmitted();
18401
  };
18402
}
18403
 
18404
/**
18405
 * @ngdoc directive
18406
 * @name ngForm
18407
 * @restrict EAC
18408
 *
18409
 * @description
18410
 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
18411
 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
18412
 * sub-group of controls needs to be determined.
18413
 *
18414
 * Note: the purpose of `ngForm` is to group controls,
18415
 * but not to be a replacement for the `<form>` tag with all of its capabilities
18416
 * (e.g. posting to the server, ...).
18417
 *
18418
 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
18419
 *                       related scope, under this name.
18420
 *
18421
 */
18422
 
18423
 /**
18424
 * @ngdoc directive
18425
 * @name form
18426
 * @restrict E
18427
 *
18428
 * @description
18429
 * Directive that instantiates
18430
 * {@link form.FormController FormController}.
18431
 *
18432
 * If the `name` attribute is specified, the form controller is published onto the current scope under
18433
 * this name.
18434
 *
18435
 * # Alias: {@link ng.directive:ngForm `ngForm`}
18436
 *
18437
 * In Angular forms can be nested. This means that the outer form is valid when all of the child
18438
 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
18439
 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
18440
 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
18441
 * using Angular validation directives in forms that are dynamically generated using the
18442
 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
18443
 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
18444
 * `ngForm` directive and nest these in an outer `form` element.
18445
 *
18446
 *
18447
 * # CSS classes
18448
 *  - `ng-valid` is set if the form is valid.
18449
 *  - `ng-invalid` is set if the form is invalid.
18450
 *  - `ng-pristine` is set if the form is pristine.
18451
 *  - `ng-dirty` is set if the form is dirty.
18452
 *  - `ng-submitted` is set if the form was submitted.
18453
 *
18454
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
18455
 *
18456
 *
18457
 * # Submitting a form and preventing the default action
18458
 *
18459
 * Since the role of forms in client-side Angular applications is different than in classical
18460
 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
18461
 * page reload that sends the data to the server. Instead some javascript logic should be triggered
18462
 * to handle the form submission in an application-specific way.
18463
 *
18464
 * For this reason, Angular prevents the default action (form submission to the server) unless the
18465
 * `<form>` element has an `action` attribute specified.
18466
 *
18467
 * You can use one of the following two ways to specify what javascript method should be called when
18468
 * a form is submitted:
18469
 *
18470
 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
18471
 * - {@link ng.directive:ngClick ngClick} directive on the first
18472
  *  button or input field of type submit (input[type=submit])
18473
 *
18474
 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
18475
 * or {@link ng.directive:ngClick ngClick} directives.
18476
 * This is because of the following form submission rules in the HTML specification:
18477
 *
18478
 * - If a form has only one input field then hitting enter in this field triggers form submit
18479
 * (`ngSubmit`)
18480
 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
18481
 * doesn't trigger submit
18482
 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
18483
 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
18484
 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
18485
 *
18486
 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
18487
 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
18488
 * to have access to the updated model.
18489
 *
18490
 * ## Animation Hooks
18491
 *
18492
 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
18493
 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
18494
 * other validations that are performed within the form. Animations in ngForm are similar to how
18495
 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
18496
 * as JS animations.
18497
 *
18498
 * The following example shows a simple way to utilize CSS transitions to style a form element
18499
 * that has been rendered as invalid after it has been validated:
18500
 *
18501
 * <pre>
18502
 * //be sure to include ngAnimate as a module to hook into more
18503
 * //advanced animations
18504
 * .my-form {
18505
 *   transition:0.5s linear all;
18506
 *   background: white;
18507
 * }
18508
 * .my-form.ng-invalid {
18509
 *   background: red;
18510
 *   color:white;
18511
 * }
18512
 * </pre>
18513
 *
18514
 * @example
18515
    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
18516
      <file name="index.html">
18517
       <script>
18518
         angular.module('formExample', [])
18519
           .controller('FormController', ['$scope', function($scope) {
18520
             $scope.userType = 'guest';
18521
           }]);
18522
       </script>
18523
       <style>
18524
        .my-form {
18525
          -webkit-transition:all linear 0.5s;
18526
          transition:all linear 0.5s;
18527
          background: transparent;
18528
        }
18529
        .my-form.ng-invalid {
18530
          background: red;
18531
        }
18532
       </style>
18533
       <form name="myForm" ng-controller="FormController" class="my-form">
18534
         userType: <input name="input" ng-model="userType" required>
18535
         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
18536
         <tt>userType = {{userType}}</tt><br>
18537
         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
18538
         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
18539
         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
18540
         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
18541
        </form>
18542
      </file>
18543
      <file name="protractor.js" type="protractor">
18544
        it('should initialize to model', function() {
18545
          var userType = element(by.binding('userType'));
18546
          var valid = element(by.binding('myForm.input.$valid'));
18547
 
18548
          expect(userType.getText()).toContain('guest');
18549
          expect(valid.getText()).toContain('true');
18550
        });
18551
 
18552
        it('should be invalid if empty', function() {
18553
          var userType = element(by.binding('userType'));
18554
          var valid = element(by.binding('myForm.input.$valid'));
18555
          var userInput = element(by.model('userType'));
18556
 
18557
          userInput.clear();
18558
          userInput.sendKeys('');
18559
 
18560
          expect(userType.getText()).toEqual('userType =');
18561
          expect(valid.getText()).toContain('false');
18562
        });
18563
      </file>
18564
    </example>
18565
 *
18566
 * @param {string=} name Name of the form. If specified, the form controller will be published into
18567
 *                       related scope, under this name.
18568
 */
18569
var formDirectiveFactory = function(isNgForm) {
18570
  return ['$timeout', function($timeout) {
18571
    var formDirective = {
18572
      name: 'form',
18573
      restrict: isNgForm ? 'EAC' : 'E',
18574
      controller: FormController,
18575
      compile: function ngFormCompile(formElement) {
18576
        // Setup initial state of the control
18577
        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
18578
 
18579
        return {
18580
          pre: function ngFormPreLink(scope, formElement, attr, controller) {
18581
            // if `action` attr is not present on the form, prevent the default action (submission)
18582
            if (!('action' in attr)) {
18583
              // we can't use jq events because if a form is destroyed during submission the default
18584
              // action is not prevented. see #1238
18585
              //
18586
              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
18587
              // page reload if the form was destroyed by submission of the form via a click handler
18588
              // on a button in the form. Looks like an IE9 specific bug.
18589
              var handleFormSubmission = function(event) {
18590
                scope.$apply(function() {
18591
                  controller.$commitViewValue();
18592
                  controller.$setSubmitted();
18593
                });
18594
 
18595
                event.preventDefault();
18596
              };
18597
 
18598
              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18599
 
18600
              // unregister the preventDefault listener so that we don't not leak memory but in a
18601
              // way that will achieve the prevention of the default action.
18602
              formElement.on('$destroy', function() {
18603
                $timeout(function() {
18604
                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18605
                }, 0, false);
18606
              });
18607
            }
18608
 
18609
            var parentFormCtrl = controller.$$parentForm,
18610
                alias = controller.$name;
18611
 
18612
            if (alias) {
18613
              setter(scope, null, alias, controller, alias);
18614
              attr.$observe(attr.name ? 'name' : 'ngForm', function(newValue) {
18615
                if (alias === newValue) return;
18616
                setter(scope, null, alias, undefined, alias);
18617
                alias = newValue;
18618
                setter(scope, null, alias, controller, alias);
18619
                parentFormCtrl.$$renameControl(controller, alias);
18620
              });
18621
            }
18622
            formElement.on('$destroy', function() {
18623
              parentFormCtrl.$removeControl(controller);
18624
              if (alias) {
18625
                setter(scope, null, alias, undefined, alias);
18626
              }
18627
              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
18628
            });
18629
          }
18630
        };
18631
      }
18632
    };
18633
 
18634
    return formDirective;
18635
  }];
18636
};
18637
 
18638
var formDirective = formDirectiveFactory();
18639
var ngFormDirective = formDirectiveFactory(true);
18640
 
18641
/* global VALID_CLASS: false,
18642
  INVALID_CLASS: false,
18643
  PRISTINE_CLASS: false,
18644
  DIRTY_CLASS: false,
18645
  UNTOUCHED_CLASS: false,
18646
  TOUCHED_CLASS: false,
18647
  $ngModelMinErr: false,
18648
*/
18649
 
18650
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
18651
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)/;
18652
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
18653
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;
18654
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
18655
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
18656
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18657
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
18658
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
18659
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18660
 
18661
var inputType = {
18662
 
18663
  /**
18664
   * @ngdoc input
18665
   * @name input[text]
18666
   *
18667
   * @description
18668
   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
18669
   *
18670
   *
18671
   * @param {string} ngModel Assignable angular expression to data-bind to.
18672
   * @param {string=} name Property name of the form under which the control is published.
18673
   * @param {string=} required Adds `required` validation error key if the value is not entered.
18674
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18675
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18676
   *    `required` when you want to data-bind to the `required` attribute.
18677
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18678
   *    minlength.
18679
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18680
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18681
   *    any length.
18682
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18683
   *    that contains the regular expression body that will be converted to a regular expression
18684
   *    as in the ngPattern directive.
18685
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18686
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
18687
   *    If the expression evaluates to a RegExp object then this is used directly.
18688
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18689
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18690
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
18691
   *    interaction with the input element.
18692
   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
18693
   *    This parameter is ignored for input[type=password] controls, which will never trim the
18694
   *    input.
18695
   *
18696
   * @example
18697
      <example name="text-input-directive" module="textInputExample">
18698
        <file name="index.html">
18699
         <script>
18700
           angular.module('textInputExample', [])
18701
             .controller('ExampleController', ['$scope', function($scope) {
18702
               $scope.example = {
18703
                 text: 'guest',
18704
                 word: /^\s*\w*\s*$/
18705
               };
18706
             }]);
18707
         </script>
18708
         <form name="myForm" ng-controller="ExampleController">
18709
           Single word: <input type="text" name="input" ng-model="example.text"
18710
                               ng-pattern="example.word" required ng-trim="false">
18711
           <span class="error" ng-show="myForm.input.$error.required">
18712
             Required!</span>
18713
           <span class="error" ng-show="myForm.input.$error.pattern">
18714
             Single word only!</span>
18715
 
18716
           <tt>text = {{example.text}}</tt><br/>
18717
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18718
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18719
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18720
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18721
          </form>
18722
        </file>
18723
        <file name="protractor.js" type="protractor">
18724
          var text = element(by.binding('example.text'));
18725
          var valid = element(by.binding('myForm.input.$valid'));
18726
          var input = element(by.model('example.text'));
18727
 
18728
          it('should initialize to model', function() {
18729
            expect(text.getText()).toContain('guest');
18730
            expect(valid.getText()).toContain('true');
18731
          });
18732
 
18733
          it('should be invalid if empty', function() {
18734
            input.clear();
18735
            input.sendKeys('');
18736
 
18737
            expect(text.getText()).toEqual('text =');
18738
            expect(valid.getText()).toContain('false');
18739
          });
18740
 
18741
          it('should be invalid if multi word', function() {
18742
            input.clear();
18743
            input.sendKeys('hello world');
18744
 
18745
            expect(valid.getText()).toContain('false');
18746
          });
18747
        </file>
18748
      </example>
18749
   */
18750
  'text': textInputType,
18751
 
18752
    /**
18753
     * @ngdoc input
18754
     * @name input[date]
18755
     *
18756
     * @description
18757
     * Input with date validation and transformation. In browsers that do not yet support
18758
     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
18759
     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
18760
     * modern browsers do not yet support this input type, it is important to provide cues to users on the
18761
     * expected input format via a placeholder or label.
18762
     *
18763
     * The model must always be a Date object, otherwise Angular will throw an error.
18764
     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18765
     *
18766
     * The timezone to be used to read/write the `Date` instance in the model can be defined using
18767
     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18768
     *
18769
     * @param {string} ngModel Assignable angular expression to data-bind to.
18770
     * @param {string=} name Property name of the form under which the control is published.
18771
     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18772
     * valid ISO date string (yyyy-MM-dd).
18773
     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18774
     * a valid ISO date string (yyyy-MM-dd).
18775
     * @param {string=} required Sets `required` validation error key if the value is not entered.
18776
     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18777
     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18778
     *    `required` when you want to data-bind to the `required` attribute.
18779
     * @param {string=} ngChange Angular expression to be executed when input changes due to user
18780
     *    interaction with the input element.
18781
     *
18782
     * @example
18783
     <example name="date-input-directive" module="dateInputExample">
18784
     <file name="index.html">
18785
       <script>
18786
          angular.module('dateInputExample', [])
18787
            .controller('DateController', ['$scope', function($scope) {
18788
              $scope.example = {
18789
                value: new Date(2013, 9, 22)
18790
              };
18791
            }]);
18792
       </script>
18793
       <form name="myForm" ng-controller="DateController as dateCtrl">
18794
          Pick a date in 2013:
18795
          <input type="date" id="exampleInput" name="input" ng-model="example.value"
18796
              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
18797
          <span class="error" ng-show="myForm.input.$error.required">
18798
              Required!</span>
18799
          <span class="error" ng-show="myForm.input.$error.date">
18800
              Not a valid date!</span>
18801
           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
18802
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18803
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18804
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18805
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18806
       </form>
18807
     </file>
18808
     <file name="protractor.js" type="protractor">
18809
        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
18810
        var valid = element(by.binding('myForm.input.$valid'));
18811
        var input = element(by.model('example.value'));
18812
 
18813
        // currently protractor/webdriver does not support
18814
        // sending keys to all known HTML5 input controls
18815
        // for various browsers (see https://github.com/angular/protractor/issues/562).
18816
        function setInput(val) {
18817
          // set the value of the element and force validation.
18818
          var scr = "var ipt = document.getElementById('exampleInput'); " +
18819
          "ipt.value = '" + val + "';" +
18820
          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18821
          browser.executeScript(scr);
18822
        }
18823
 
18824
        it('should initialize to model', function() {
18825
          expect(value.getText()).toContain('2013-10-22');
18826
          expect(valid.getText()).toContain('myForm.input.$valid = true');
18827
        });
18828
 
18829
        it('should be invalid if empty', function() {
18830
          setInput('');
18831
          expect(value.getText()).toEqual('value =');
18832
          expect(valid.getText()).toContain('myForm.input.$valid = false');
18833
        });
18834
 
18835
        it('should be invalid if over max', function() {
18836
          setInput('2015-01-01');
18837
          expect(value.getText()).toContain('');
18838
          expect(valid.getText()).toContain('myForm.input.$valid = false');
18839
        });
18840
     </file>
18841
     </example>
18842
     */
18843
  'date': createDateInputType('date', DATE_REGEXP,
18844
         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
18845
         'yyyy-MM-dd'),
18846
 
18847
   /**
18848
    * @ngdoc input
18849
    * @name input[datetime-local]
18850
    *
18851
    * @description
18852
    * Input with datetime validation and transformation. In browsers that do not yet support
18853
    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18854
    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
18855
    *
18856
    * The model must always be a Date object, otherwise Angular will throw an error.
18857
    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18858
    *
18859
    * The timezone to be used to read/write the `Date` instance in the model can be defined using
18860
    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18861
    *
18862
    * @param {string} ngModel Assignable angular expression to data-bind to.
18863
    * @param {string=} name Property name of the form under which the control is published.
18864
    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18865
    * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18866
    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18867
    * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18868
    * @param {string=} required Sets `required` validation error key if the value is not entered.
18869
    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18870
    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18871
    *    `required` when you want to data-bind to the `required` attribute.
18872
    * @param {string=} ngChange Angular expression to be executed when input changes due to user
18873
    *    interaction with the input element.
18874
    *
18875
    * @example
18876
    <example name="datetimelocal-input-directive" module="dateExample">
18877
    <file name="index.html">
18878
      <script>
18879
        angular.module('dateExample', [])
18880
          .controller('DateController', ['$scope', function($scope) {
18881
            $scope.example = {
18882
              value: new Date(2010, 11, 28, 14, 57)
18883
            };
18884
          }]);
18885
      </script>
18886
      <form name="myForm" ng-controller="DateController as dateCtrl">
18887
        Pick a date between in 2013:
18888
        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
18889
            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
18890
        <span class="error" ng-show="myForm.input.$error.required">
18891
            Required!</span>
18892
        <span class="error" ng-show="myForm.input.$error.datetimelocal">
18893
            Not a valid date!</span>
18894
        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
18895
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18896
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18897
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18898
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18899
      </form>
18900
    </file>
18901
    <file name="protractor.js" type="protractor">
18902
      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
18903
      var valid = element(by.binding('myForm.input.$valid'));
18904
      var input = element(by.model('example.value'));
18905
 
18906
      // currently protractor/webdriver does not support
18907
      // sending keys to all known HTML5 input controls
18908
      // for various browsers (https://github.com/angular/protractor/issues/562).
18909
      function setInput(val) {
18910
        // set the value of the element and force validation.
18911
        var scr = "var ipt = document.getElementById('exampleInput'); " +
18912
        "ipt.value = '" + val + "';" +
18913
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18914
        browser.executeScript(scr);
18915
      }
18916
 
18917
      it('should initialize to model', function() {
18918
        expect(value.getText()).toContain('2010-12-28T14:57:00');
18919
        expect(valid.getText()).toContain('myForm.input.$valid = true');
18920
      });
18921
 
18922
      it('should be invalid if empty', function() {
18923
        setInput('');
18924
        expect(value.getText()).toEqual('value =');
18925
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18926
      });
18927
 
18928
      it('should be invalid if over max', function() {
18929
        setInput('2015-01-01T23:59:00');
18930
        expect(value.getText()).toContain('');
18931
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18932
      });
18933
    </file>
18934
    </example>
18935
    */
18936
  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
18937
      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
18938
      'yyyy-MM-ddTHH:mm:ss.sss'),
18939
 
18940
  /**
18941
   * @ngdoc input
18942
   * @name input[time]
18943
   *
18944
   * @description
18945
   * Input with time validation and transformation. In browsers that do not yet support
18946
   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18947
   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
18948
   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
18949
   *
18950
   * The model must always be a Date object, otherwise Angular will throw an error.
18951
   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18952
   *
18953
   * The timezone to be used to read/write the `Date` instance in the model can be defined using
18954
   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18955
   *
18956
   * @param {string} ngModel Assignable angular expression to data-bind to.
18957
   * @param {string=} name Property name of the form under which the control is published.
18958
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18959
   * valid ISO time format (HH:mm:ss).
18960
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
18961
   * valid ISO time format (HH:mm:ss).
18962
   * @param {string=} required Sets `required` validation error key if the value is not entered.
18963
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18964
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18965
   *    `required` when you want to data-bind to the `required` attribute.
18966
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
18967
   *    interaction with the input element.
18968
   *
18969
   * @example
18970
   <example name="time-input-directive" module="timeExample">
18971
   <file name="index.html">
18972
     <script>
18973
      angular.module('timeExample', [])
18974
        .controller('DateController', ['$scope', function($scope) {
18975
          $scope.example = {
18976
            value: new Date(1970, 0, 1, 14, 57, 0)
18977
          };
18978
        }]);
18979
     </script>
18980
     <form name="myForm" ng-controller="DateController as dateCtrl">
18981
        Pick a between 8am and 5pm:
18982
        <input type="time" id="exampleInput" name="input" ng-model="example.value"
18983
            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
18984
        <span class="error" ng-show="myForm.input.$error.required">
18985
            Required!</span>
18986
        <span class="error" ng-show="myForm.input.$error.time">
18987
            Not a valid date!</span>
18988
        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
18989
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18990
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18991
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18992
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18993
     </form>
18994
   </file>
18995
   <file name="protractor.js" type="protractor">
18996
      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
18997
      var valid = element(by.binding('myForm.input.$valid'));
18998
      var input = element(by.model('example.value'));
18999
 
19000
      // currently protractor/webdriver does not support
19001
      // sending keys to all known HTML5 input controls
19002
      // for various browsers (https://github.com/angular/protractor/issues/562).
19003
      function setInput(val) {
19004
        // set the value of the element and force validation.
19005
        var scr = "var ipt = document.getElementById('exampleInput'); " +
19006
        "ipt.value = '" + val + "';" +
19007
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19008
        browser.executeScript(scr);
19009
      }
19010
 
19011
      it('should initialize to model', function() {
19012
        expect(value.getText()).toContain('14:57:00');
19013
        expect(valid.getText()).toContain('myForm.input.$valid = true');
19014
      });
19015
 
19016
      it('should be invalid if empty', function() {
19017
        setInput('');
19018
        expect(value.getText()).toEqual('value =');
19019
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19020
      });
19021
 
19022
      it('should be invalid if over max', function() {
19023
        setInput('23:59:00');
19024
        expect(value.getText()).toContain('');
19025
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19026
      });
19027
   </file>
19028
   </example>
19029
   */
19030
  'time': createDateInputType('time', TIME_REGEXP,
19031
      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
19032
     'HH:mm:ss.sss'),
19033
 
19034
   /**
19035
    * @ngdoc input
19036
    * @name input[week]
19037
    *
19038
    * @description
19039
    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
19040
    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19041
    * week format (yyyy-W##), for example: `2013-W02`.
19042
    *
19043
    * The model must always be a Date object, otherwise Angular will throw an error.
19044
    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19045
    *
19046
    * The timezone to be used to read/write the `Date` instance in the model can be defined using
19047
    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19048
    *
19049
    * @param {string} ngModel Assignable angular expression to data-bind to.
19050
    * @param {string=} name Property name of the form under which the control is published.
19051
    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19052
    * valid ISO week format (yyyy-W##).
19053
    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19054
    * a valid ISO week format (yyyy-W##).
19055
    * @param {string=} required Sets `required` validation error key if the value is not entered.
19056
    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19057
    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19058
    *    `required` when you want to data-bind to the `required` attribute.
19059
    * @param {string=} ngChange Angular expression to be executed when input changes due to user
19060
    *    interaction with the input element.
19061
    *
19062
    * @example
19063
    <example name="week-input-directive" module="weekExample">
19064
    <file name="index.html">
19065
      <script>
19066
      angular.module('weekExample', [])
19067
        .controller('DateController', ['$scope', function($scope) {
19068
          $scope.example = {
19069
            value: new Date(2013, 0, 3)
19070
          };
19071
        }]);
19072
      </script>
19073
      <form name="myForm" ng-controller="DateController as dateCtrl">
19074
        Pick a date between in 2013:
19075
        <input id="exampleInput" type="week" name="input" ng-model="example.value"
19076
            placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
19077
        <span class="error" ng-show="myForm.input.$error.required">
19078
            Required!</span>
19079
        <span class="error" ng-show="myForm.input.$error.week">
19080
            Not a valid date!</span>
19081
        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
19082
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19083
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19084
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19085
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19086
      </form>
19087
    </file>
19088
    <file name="protractor.js" type="protractor">
19089
      var value = element(by.binding('example.value | date: "yyyy-Www"'));
19090
      var valid = element(by.binding('myForm.input.$valid'));
19091
      var input = element(by.model('example.value'));
19092
 
19093
      // currently protractor/webdriver does not support
19094
      // sending keys to all known HTML5 input controls
19095
      // for various browsers (https://github.com/angular/protractor/issues/562).
19096
      function setInput(val) {
19097
        // set the value of the element and force validation.
19098
        var scr = "var ipt = document.getElementById('exampleInput'); " +
19099
        "ipt.value = '" + val + "';" +
19100
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19101
        browser.executeScript(scr);
19102
      }
19103
 
19104
      it('should initialize to model', function() {
19105
        expect(value.getText()).toContain('2013-W01');
19106
        expect(valid.getText()).toContain('myForm.input.$valid = true');
19107
      });
19108
 
19109
      it('should be invalid if empty', function() {
19110
        setInput('');
19111
        expect(value.getText()).toEqual('value =');
19112
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19113
      });
19114
 
19115
      it('should be invalid if over max', function() {
19116
        setInput('2015-W01');
19117
        expect(value.getText()).toContain('');
19118
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19119
      });
19120
    </file>
19121
    </example>
19122
    */
19123
  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
19124
 
19125
  /**
19126
   * @ngdoc input
19127
   * @name input[month]
19128
   *
19129
   * @description
19130
   * Input with month validation and transformation. In browsers that do not yet support
19131
   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19132
   * month format (yyyy-MM), for example: `2009-01`.
19133
   *
19134
   * The model must always be a Date object, otherwise Angular will throw an error.
19135
   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19136
   * If the model is not set to the first of the month, the next view to model update will set it
19137
   * to the first of the month.
19138
   *
19139
   * The timezone to be used to read/write the `Date` instance in the model can be defined using
19140
   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19141
   *
19142
   * @param {string} ngModel Assignable angular expression to data-bind to.
19143
   * @param {string=} name Property name of the form under which the control is published.
19144
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
19145
   * a valid ISO month format (yyyy-MM).
19146
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
19147
   * be a valid ISO month format (yyyy-MM).
19148
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19149
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19150
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19151
   *    `required` when you want to data-bind to the `required` attribute.
19152
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19153
   *    interaction with the input element.
19154
   *
19155
   * @example
19156
   <example name="month-input-directive" module="monthExample">
19157
   <file name="index.html">
19158
     <script>
19159
      angular.module('monthExample', [])
19160
        .controller('DateController', ['$scope', function($scope) {
19161
          $scope.example = {
19162
            value: new Date(2013, 9, 1)
19163
          };
19164
        }]);
19165
     </script>
19166
     <form name="myForm" ng-controller="DateController as dateCtrl">
19167
       Pick a month in 2013:
19168
       <input id="exampleInput" type="month" name="input" ng-model="example.value"
19169
          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
19170
       <span class="error" ng-show="myForm.input.$error.required">
19171
          Required!</span>
19172
       <span class="error" ng-show="myForm.input.$error.month">
19173
          Not a valid month!</span>
19174
       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
19175
       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19176
       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19177
       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19178
       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19179
     </form>
19180
   </file>
19181
   <file name="protractor.js" type="protractor">
19182
      var value = element(by.binding('example.value | date: "yyyy-MM"'));
19183
      var valid = element(by.binding('myForm.input.$valid'));
19184
      var input = element(by.model('example.value'));
19185
 
19186
      // currently protractor/webdriver does not support
19187
      // sending keys to all known HTML5 input controls
19188
      // for various browsers (https://github.com/angular/protractor/issues/562).
19189
      function setInput(val) {
19190
        // set the value of the element and force validation.
19191
        var scr = "var ipt = document.getElementById('exampleInput'); " +
19192
        "ipt.value = '" + val + "';" +
19193
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19194
        browser.executeScript(scr);
19195
      }
19196
 
19197
      it('should initialize to model', function() {
19198
        expect(value.getText()).toContain('2013-10');
19199
        expect(valid.getText()).toContain('myForm.input.$valid = true');
19200
      });
19201
 
19202
      it('should be invalid if empty', function() {
19203
        setInput('');
19204
        expect(value.getText()).toEqual('value =');
19205
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19206
      });
19207
 
19208
      it('should be invalid if over max', function() {
19209
        setInput('2015-01');
19210
        expect(value.getText()).toContain('');
19211
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19212
      });
19213
   </file>
19214
   </example>
19215
   */
19216
  'month': createDateInputType('month', MONTH_REGEXP,
19217
     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
19218
     'yyyy-MM'),
19219
 
19220
  /**
19221
   * @ngdoc input
19222
   * @name input[number]
19223
   *
19224
   * @description
19225
   * Text input with number validation and transformation. Sets the `number` validation
19226
   * error if not a valid number.
19227
   *
19228
   * The model must always be a number, otherwise Angular will throw an error.
19229
   *
19230
   * @param {string} ngModel Assignable angular expression to data-bind to.
19231
   * @param {string=} name Property name of the form under which the control is published.
19232
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
19233
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
19234
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19235
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19236
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19237
   *    `required` when you want to data-bind to the `required` attribute.
19238
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19239
   *    minlength.
19240
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19241
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19242
   *    any length.
19243
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19244
   *    that contains the regular expression body that will be converted to a regular expression
19245
   *    as in the ngPattern directive.
19246
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19247
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19248
   *    If the expression evaluates to a RegExp object then this is used directly.
19249
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19250
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19251
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19252
   *    interaction with the input element.
19253
   *
19254
   * @example
19255
      <example name="number-input-directive" module="numberExample">
19256
        <file name="index.html">
19257
         <script>
19258
           angular.module('numberExample', [])
19259
             .controller('ExampleController', ['$scope', function($scope) {
19260
               $scope.example = {
19261
                 value: 12
19262
               };
19263
             }]);
19264
         </script>
19265
         <form name="myForm" ng-controller="ExampleController">
19266
           Number: <input type="number" name="input" ng-model="example.value"
19267
                          min="0" max="99" required>
19268
           <span class="error" ng-show="myForm.input.$error.required">
19269
             Required!</span>
19270
           <span class="error" ng-show="myForm.input.$error.number">
19271
             Not valid number!</span>
19272
           <tt>value = {{example.value}}</tt><br/>
19273
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19274
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19275
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19276
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19277
          </form>
19278
        </file>
19279
        <file name="protractor.js" type="protractor">
19280
          var value = element(by.binding('example.value'));
19281
          var valid = element(by.binding('myForm.input.$valid'));
19282
          var input = element(by.model('example.value'));
19283
 
19284
          it('should initialize to model', function() {
19285
            expect(value.getText()).toContain('12');
19286
            expect(valid.getText()).toContain('true');
19287
          });
19288
 
19289
          it('should be invalid if empty', function() {
19290
            input.clear();
19291
            input.sendKeys('');
19292
            expect(value.getText()).toEqual('value =');
19293
            expect(valid.getText()).toContain('false');
19294
          });
19295
 
19296
          it('should be invalid if over max', function() {
19297
            input.clear();
19298
            input.sendKeys('123');
19299
            expect(value.getText()).toEqual('value =');
19300
            expect(valid.getText()).toContain('false');
19301
          });
19302
        </file>
19303
      </example>
19304
   */
19305
  'number': numberInputType,
19306
 
19307
 
19308
  /**
19309
   * @ngdoc input
19310
   * @name input[url]
19311
   *
19312
   * @description
19313
   * Text input with URL validation. Sets the `url` validation error key if the content is not a
19314
   * valid URL.
19315
   *
19316
   * <div class="alert alert-warning">
19317
   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
19318
   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
19319
   * the built-in validators (see the {@link guide/forms Forms guide})
19320
   * </div>
19321
   *
19322
   * @param {string} ngModel Assignable angular expression to data-bind to.
19323
   * @param {string=} name Property name of the form under which the control is published.
19324
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19325
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19326
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19327
   *    `required` when you want to data-bind to the `required` attribute.
19328
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19329
   *    minlength.
19330
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19331
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19332
   *    any length.
19333
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19334
   *    that contains the regular expression body that will be converted to a regular expression
19335
   *    as in the ngPattern directive.
19336
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19337
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19338
   *    If the expression evaluates to a RegExp object then this is used directly.
19339
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19340
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19341
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19342
   *    interaction with the input element.
19343
   *
19344
   * @example
19345
      <example name="url-input-directive" module="urlExample">
19346
        <file name="index.html">
19347
         <script>
19348
           angular.module('urlExample', [])
19349
             .controller('ExampleController', ['$scope', function($scope) {
19350
               $scope.url = {
19351
                 text: 'http://google.com'
19352
               };
19353
             }]);
19354
         </script>
19355
         <form name="myForm" ng-controller="ExampleController">
19356
           URL: <input type="url" name="input" ng-model="url.text" required>
19357
           <span class="error" ng-show="myForm.input.$error.required">
19358
             Required!</span>
19359
           <span class="error" ng-show="myForm.input.$error.url">
19360
             Not valid url!</span>
19361
           <tt>text = {{url.text}}</tt><br/>
19362
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19363
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19364
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19365
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19366
           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
19367
          </form>
19368
        </file>
19369
        <file name="protractor.js" type="protractor">
19370
          var text = element(by.binding('url.text'));
19371
          var valid = element(by.binding('myForm.input.$valid'));
19372
          var input = element(by.model('url.text'));
19373
 
19374
          it('should initialize to model', function() {
19375
            expect(text.getText()).toContain('http://google.com');
19376
            expect(valid.getText()).toContain('true');
19377
          });
19378
 
19379
          it('should be invalid if empty', function() {
19380
            input.clear();
19381
            input.sendKeys('');
19382
 
19383
            expect(text.getText()).toEqual('text =');
19384
            expect(valid.getText()).toContain('false');
19385
          });
19386
 
19387
          it('should be invalid if not url', function() {
19388
            input.clear();
19389
            input.sendKeys('box');
19390
 
19391
            expect(valid.getText()).toContain('false');
19392
          });
19393
        </file>
19394
      </example>
19395
   */
19396
  'url': urlInputType,
19397
 
19398
 
19399
  /**
19400
   * @ngdoc input
19401
   * @name input[email]
19402
   *
19403
   * @description
19404
   * Text input with email validation. Sets the `email` validation error key if not a valid email
19405
   * address.
19406
   *
19407
   * <div class="alert alert-warning">
19408
   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
19409
   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
19410
   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
19411
   * </div>
19412
   *
19413
   * @param {string} ngModel Assignable angular expression to data-bind to.
19414
   * @param {string=} name Property name of the form under which the control is published.
19415
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19416
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19417
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19418
   *    `required` when you want to data-bind to the `required` attribute.
19419
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19420
   *    minlength.
19421
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19422
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19423
   *    any length.
19424
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19425
   *    that contains the regular expression body that will be converted to a regular expression
19426
   *    as in the ngPattern directive.
19427
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19428
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19429
   *    If the expression evaluates to a RegExp object then this is used directly.
19430
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19431
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19432
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19433
   *    interaction with the input element.
19434
   *
19435
   * @example
19436
      <example name="email-input-directive" module="emailExample">
19437
        <file name="index.html">
19438
         <script>
19439
           angular.module('emailExample', [])
19440
             .controller('ExampleController', ['$scope', function($scope) {
19441
               $scope.email = {
19442
                 text: 'me@example.com'
19443
               };
19444
             }]);
19445
         </script>
19446
           <form name="myForm" ng-controller="ExampleController">
19447
             Email: <input type="email" name="input" ng-model="email.text" required>
19448
             <span class="error" ng-show="myForm.input.$error.required">
19449
               Required!</span>
19450
             <span class="error" ng-show="myForm.input.$error.email">
19451
               Not valid email!</span>
19452
             <tt>text = {{email.text}}</tt><br/>
19453
             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19454
             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19455
             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19456
             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19457
             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
19458
           </form>
19459
         </file>
19460
        <file name="protractor.js" type="protractor">
19461
          var text = element(by.binding('email.text'));
19462
          var valid = element(by.binding('myForm.input.$valid'));
19463
          var input = element(by.model('email.text'));
19464
 
19465
          it('should initialize to model', function() {
19466
            expect(text.getText()).toContain('me@example.com');
19467
            expect(valid.getText()).toContain('true');
19468
          });
19469
 
19470
          it('should be invalid if empty', function() {
19471
            input.clear();
19472
            input.sendKeys('');
19473
            expect(text.getText()).toEqual('text =');
19474
            expect(valid.getText()).toContain('false');
19475
          });
19476
 
19477
          it('should be invalid if not email', function() {
19478
            input.clear();
19479
            input.sendKeys('xxx');
19480
 
19481
            expect(valid.getText()).toContain('false');
19482
          });
19483
        </file>
19484
      </example>
19485
   */
19486
  'email': emailInputType,
19487
 
19488
 
19489
  /**
19490
   * @ngdoc input
19491
   * @name input[radio]
19492
   *
19493
   * @description
19494
   * HTML radio button.
19495
   *
19496
   * @param {string} ngModel Assignable angular expression to data-bind to.
19497
   * @param {string} value The value to which the expression should be set when selected.
19498
   * @param {string=} name Property name of the form under which the control is published.
19499
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19500
   *    interaction with the input element.
19501
   * @param {string} ngValue Angular expression which sets the value to which the expression should
19502
   *    be set when selected.
19503
   *
19504
   * @example
19505
      <example name="radio-input-directive" module="radioExample">
19506
        <file name="index.html">
19507
         <script>
19508
           angular.module('radioExample', [])
19509
             .controller('ExampleController', ['$scope', function($scope) {
19510
               $scope.color = {
19511
                 name: 'blue'
19512
               };
19513
               $scope.specialValue = {
19514
                 "id": "12345",
19515
                 "value": "green"
19516
               };
19517
             }]);
19518
         </script>
19519
         <form name="myForm" ng-controller="ExampleController">
19520
           <input type="radio" ng-model="color.name" value="red">  Red <br/>
19521
           <input type="radio" ng-model="color.name" ng-value="specialValue"> Green <br/>
19522
           <input type="radio" ng-model="color.name" value="blue"> Blue <br/>
19523
           <tt>color = {{color.name | json}}</tt><br/>
19524
          </form>
19525
          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
19526
        </file>
19527
        <file name="protractor.js" type="protractor">
19528
          it('should change state', function() {
19529
            var color = element(by.binding('color.name'));
19530
 
19531
            expect(color.getText()).toContain('blue');
19532
 
19533
            element.all(by.model('color.name')).get(0).click();
19534
 
19535
            expect(color.getText()).toContain('red');
19536
          });
19537
        </file>
19538
      </example>
19539
   */
19540
  'radio': radioInputType,
19541
 
19542
 
19543
  /**
19544
   * @ngdoc input
19545
   * @name input[checkbox]
19546
   *
19547
   * @description
19548
   * HTML checkbox.
19549
   *
19550
   * @param {string} ngModel Assignable angular expression to data-bind to.
19551
   * @param {string=} name Property name of the form under which the control is published.
19552
   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
19553
   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
19554
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19555
   *    interaction with the input element.
19556
   *
19557
   * @example
19558
      <example name="checkbox-input-directive" module="checkboxExample">
19559
        <file name="index.html">
19560
         <script>
19561
           angular.module('checkboxExample', [])
19562
             .controller('ExampleController', ['$scope', function($scope) {
19563
               $scope.checkboxModel = {
19564
                value1 : true,
19565
                value2 : 'YES'
19566
              };
19567
             }]);
19568
         </script>
19569
         <form name="myForm" ng-controller="ExampleController">
19570
           Value1: <input type="checkbox" ng-model="checkboxModel.value1"> <br/>
19571
           Value2: <input type="checkbox" ng-model="checkboxModel.value2"
19572
                          ng-true-value="'YES'" ng-false-value="'NO'"> <br/>
19573
           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
19574
           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
19575
          </form>
19576
        </file>
19577
        <file name="protractor.js" type="protractor">
19578
          it('should change state', function() {
19579
            var value1 = element(by.binding('checkboxModel.value1'));
19580
            var value2 = element(by.binding('checkboxModel.value2'));
19581
 
19582
            expect(value1.getText()).toContain('true');
19583
            expect(value2.getText()).toContain('YES');
19584
 
19585
            element(by.model('checkboxModel.value1')).click();
19586
            element(by.model('checkboxModel.value2')).click();
19587
 
19588
            expect(value1.getText()).toContain('false');
19589
            expect(value2.getText()).toContain('NO');
19590
          });
19591
        </file>
19592
      </example>
19593
   */
19594
  'checkbox': checkboxInputType,
19595
 
19596
  'hidden': noop,
19597
  'button': noop,
19598
  'submit': noop,
19599
  'reset': noop,
19600
  'file': noop
19601
};
19602
 
19603
function stringBasedInputType(ctrl) {
19604
  ctrl.$formatters.push(function(value) {
19605
    return ctrl.$isEmpty(value) ? value : value.toString();
19606
  });
19607
}
19608
 
19609
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19610
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19611
  stringBasedInputType(ctrl);
19612
}
19613
 
19614
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19615
  var type = lowercase(element[0].type);
19616
 
19617
  // In composition mode, users are still inputing intermediate text buffer,
19618
  // hold the listener until composition is done.
19619
  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
19620
  if (!$sniffer.android) {
19621
    var composing = false;
19622
 
19623
    element.on('compositionstart', function(data) {
19624
      composing = true;
19625
    });
19626
 
19627
    element.on('compositionend', function() {
19628
      composing = false;
19629
      listener();
19630
    });
19631
  }
19632
 
19633
  var listener = function(ev) {
19634
    if (timeout) {
19635
      $browser.defer.cancel(timeout);
19636
      timeout = null;
19637
    }
19638
    if (composing) return;
19639
    var value = element.val(),
19640
        event = ev && ev.type;
19641
 
19642
    // By default we will trim the value
19643
    // If the attribute ng-trim exists we will avoid trimming
19644
    // If input type is 'password', the value is never trimmed
19645
    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
19646
      value = trim(value);
19647
    }
19648
 
19649
    // If a control is suffering from bad input (due to native validators), browsers discard its
19650
    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
19651
    // control's value is the same empty value twice in a row.
19652
    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
19653
      ctrl.$setViewValue(value, event);
19654
    }
19655
  };
19656
 
19657
  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
19658
  // input event on backspace, delete or cut
19659
  if ($sniffer.hasEvent('input')) {
19660
    element.on('input', listener);
19661
  } else {
19662
    var timeout;
19663
 
19664
    var deferListener = function(ev, input, origValue) {
19665
      if (!timeout) {
19666
        timeout = $browser.defer(function() {
19667
          timeout = null;
19668
          if (!input || input.value !== origValue) {
19669
            listener(ev);
19670
          }
19671
        });
19672
      }
19673
    };
19674
 
19675
    element.on('keydown', function(event) {
19676
      var key = event.keyCode;
19677
 
19678
      // ignore
19679
      //    command            modifiers                   arrows
19680
      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
19681
 
19682
      deferListener(event, this, this.value);
19683
    });
19684
 
19685
    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
19686
    if ($sniffer.hasEvent('paste')) {
19687
      element.on('paste cut', deferListener);
19688
    }
19689
  }
19690
 
19691
  // if user paste into input using mouse on older browser
19692
  // or form autocomplete on newer browser, we need "change" event to catch it
19693
  element.on('change', listener);
19694
 
19695
  ctrl.$render = function() {
19696
    element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19697
  };
19698
}
19699
 
19700
function weekParser(isoWeek, existingDate) {
19701
  if (isDate(isoWeek)) {
19702
    return isoWeek;
19703
  }
19704
 
19705
  if (isString(isoWeek)) {
19706
    WEEK_REGEXP.lastIndex = 0;
19707
    var parts = WEEK_REGEXP.exec(isoWeek);
19708
    if (parts) {
19709
      var year = +parts[1],
19710
          week = +parts[2],
19711
          hours = 0,
19712
          minutes = 0,
19713
          seconds = 0,
19714
          milliseconds = 0,
19715
          firstThurs = getFirstThursdayOfYear(year),
19716
          addDays = (week - 1) * 7;
19717
 
19718
      if (existingDate) {
19719
        hours = existingDate.getHours();
19720
        minutes = existingDate.getMinutes();
19721
        seconds = existingDate.getSeconds();
19722
        milliseconds = existingDate.getMilliseconds();
19723
      }
19724
 
19725
      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
19726
    }
19727
  }
19728
 
19729
  return NaN;
19730
}
19731
 
19732
function createDateParser(regexp, mapping) {
19733
  return function(iso, date) {
19734
    var parts, map;
19735
 
19736
    if (isDate(iso)) {
19737
      return iso;
19738
    }
19739
 
19740
    if (isString(iso)) {
19741
      // When a date is JSON'ified to wraps itself inside of an extra
19742
      // set of double quotes. This makes the date parsing code unable
19743
      // to match the date string and parse it as a date.
19744
      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
19745
        iso = iso.substring(1, iso.length - 1);
19746
      }
19747
      if (ISO_DATE_REGEXP.test(iso)) {
19748
        return new Date(iso);
19749
      }
19750
      regexp.lastIndex = 0;
19751
      parts = regexp.exec(iso);
19752
 
19753
      if (parts) {
19754
        parts.shift();
19755
        if (date) {
19756
          map = {
19757
            yyyy: date.getFullYear(),
19758
            MM: date.getMonth() + 1,
19759
            dd: date.getDate(),
19760
            HH: date.getHours(),
19761
            mm: date.getMinutes(),
19762
            ss: date.getSeconds(),
19763
            sss: date.getMilliseconds() / 1000
19764
          };
19765
        } else {
19766
          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
19767
        }
19768
 
19769
        forEach(parts, function(part, index) {
19770
          if (index < mapping.length) {
19771
            map[mapping[index]] = +part;
19772
          }
19773
        });
19774
        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
19775
      }
19776
    }
19777
 
19778
    return NaN;
19779
  };
19780
}
19781
 
19782
function createDateInputType(type, regexp, parseDate, format) {
19783
  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
19784
    badInputChecker(scope, element, attr, ctrl);
19785
    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19786
    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
19787
    var previousDate;
19788
 
19789
    ctrl.$$parserName = type;
19790
    ctrl.$parsers.push(function(value) {
19791
      if (ctrl.$isEmpty(value)) return null;
19792
      if (regexp.test(value)) {
19793
        // Note: We cannot read ctrl.$modelValue, as there might be a different
19794
        // parser/formatter in the processing chain so that the model
19795
        // contains some different data format!
19796
        var parsedDate = parseDate(value, previousDate);
19797
        if (timezone === 'UTC') {
19798
          parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
19799
        }
19800
        return parsedDate;
19801
      }
19802
      return undefined;
19803
    });
19804
 
19805
    ctrl.$formatters.push(function(value) {
19806
      if (value && !isDate(value)) {
19807
        throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19808
      }
19809
      if (isValidDate(value)) {
19810
        previousDate = value;
19811
        if (previousDate && timezone === 'UTC') {
19812
          var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
19813
          previousDate = new Date(previousDate.getTime() + timezoneOffset);
19814
        }
19815
        return $filter('date')(value, format, timezone);
19816
      } else {
19817
        previousDate = null;
19818
        return '';
19819
      }
19820
    });
19821
 
19822
    if (isDefined(attr.min) || attr.ngMin) {
19823
      var minVal;
19824
      ctrl.$validators.min = function(value) {
19825
        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19826
      };
19827
      attr.$observe('min', function(val) {
19828
        minVal = parseObservedDateValue(val);
19829
        ctrl.$validate();
19830
      });
19831
    }
19832
 
19833
    if (isDefined(attr.max) || attr.ngMax) {
19834
      var maxVal;
19835
      ctrl.$validators.max = function(value) {
19836
        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19837
      };
19838
      attr.$observe('max', function(val) {
19839
        maxVal = parseObservedDateValue(val);
19840
        ctrl.$validate();
19841
      });
19842
    }
19843
 
19844
    function isValidDate(value) {
19845
      // Invalid Date: getTime() returns NaN
19846
      return value && !(value.getTime && value.getTime() !== value.getTime());
19847
    }
19848
 
19849
    function parseObservedDateValue(val) {
19850
      return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
19851
    }
19852
  };
19853
}
19854
 
19855
function badInputChecker(scope, element, attr, ctrl) {
19856
  var node = element[0];
19857
  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
19858
  if (nativeValidation) {
19859
    ctrl.$parsers.push(function(value) {
19860
      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
19861
      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
19862
      // - also sets validity.badInput (should only be validity.typeMismatch).
19863
      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
19864
      // - can ignore this case as we can still read out the erroneous email...
19865
      return validity.badInput && !validity.typeMismatch ? undefined : value;
19866
    });
19867
  }
19868
}
19869
 
19870
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19871
  badInputChecker(scope, element, attr, ctrl);
19872
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19873
 
19874
  ctrl.$$parserName = 'number';
19875
  ctrl.$parsers.push(function(value) {
19876
    if (ctrl.$isEmpty(value))      return null;
19877
    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
19878
    return undefined;
19879
  });
19880
 
19881
  ctrl.$formatters.push(function(value) {
19882
    if (!ctrl.$isEmpty(value)) {
19883
      if (!isNumber(value)) {
19884
        throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
19885
      }
19886
      value = value.toString();
19887
    }
19888
    return value;
19889
  });
19890
 
19891
  if (attr.min || attr.ngMin) {
19892
    var minVal;
19893
    ctrl.$validators.min = function(value) {
19894
      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
19895
    };
19896
 
19897
    attr.$observe('min', function(val) {
19898
      if (isDefined(val) && !isNumber(val)) {
19899
        val = parseFloat(val, 10);
19900
      }
19901
      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
19902
      // TODO(matsko): implement validateLater to reduce number of validations
19903
      ctrl.$validate();
19904
    });
19905
  }
19906
 
19907
  if (attr.max || attr.ngMax) {
19908
    var maxVal;
19909
    ctrl.$validators.max = function(value) {
19910
      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
19911
    };
19912
 
19913
    attr.$observe('max', function(val) {
19914
      if (isDefined(val) && !isNumber(val)) {
19915
        val = parseFloat(val, 10);
19916
      }
19917
      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
19918
      // TODO(matsko): implement validateLater to reduce number of validations
19919
      ctrl.$validate();
19920
    });
19921
  }
19922
}
19923
 
19924
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19925
  // Note: no badInputChecker here by purpose as `url` is only a validation
19926
  // in browsers, i.e. we can always read out input.value even if it is not valid!
19927
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19928
  stringBasedInputType(ctrl);
19929
 
19930
  ctrl.$$parserName = 'url';
19931
  ctrl.$validators.url = function(modelValue, viewValue) {
19932
    var value = modelValue || viewValue;
19933
    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
19934
  };
19935
}
19936
 
19937
function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19938
  // Note: no badInputChecker here by purpose as `url` is only a validation
19939
  // in browsers, i.e. we can always read out input.value even if it is not valid!
19940
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19941
  stringBasedInputType(ctrl);
19942
 
19943
  ctrl.$$parserName = 'email';
19944
  ctrl.$validators.email = function(modelValue, viewValue) {
19945
    var value = modelValue || viewValue;
19946
    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
19947
  };
19948
}
19949
 
19950
function radioInputType(scope, element, attr, ctrl) {
19951
  // make the name unique, if not defined
19952
  if (isUndefined(attr.name)) {
19953
    element.attr('name', nextUid());
19954
  }
19955
 
19956
  var listener = function(ev) {
19957
    if (element[0].checked) {
19958
      ctrl.$setViewValue(attr.value, ev && ev.type);
19959
    }
19960
  };
19961
 
19962
  element.on('click', listener);
19963
 
19964
  ctrl.$render = function() {
19965
    var value = attr.value;
19966
    element[0].checked = (value == ctrl.$viewValue);
19967
  };
19968
 
19969
  attr.$observe('value', ctrl.$render);
19970
}
19971
 
19972
function parseConstantExpr($parse, context, name, expression, fallback) {
19973
  var parseFn;
19974
  if (isDefined(expression)) {
19975
    parseFn = $parse(expression);
19976
    if (!parseFn.constant) {
19977
      throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
19978
                                   '`{1}`.', name, expression);
19979
    }
19980
    return parseFn(context);
19981
  }
19982
  return fallback;
19983
}
19984
 
19985
function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
19986
  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
19987
  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
19988
 
19989
  var listener = function(ev) {
19990
    ctrl.$setViewValue(element[0].checked, ev && ev.type);
19991
  };
19992
 
19993
  element.on('click', listener);
19994
 
19995
  ctrl.$render = function() {
19996
    element[0].checked = ctrl.$viewValue;
19997
  };
19998
 
19999
  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
20000
  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
20001
  // it to a boolean.
20002
  ctrl.$isEmpty = function(value) {
20003
    return value === false;
20004
  };
20005
 
20006
  ctrl.$formatters.push(function(value) {
20007
    return equals(value, trueValue);
20008
  });
20009
 
20010
  ctrl.$parsers.push(function(value) {
20011
    return value ? trueValue : falseValue;
20012
  });
20013
}
20014
 
20015
 
20016
/**
20017
 * @ngdoc directive
20018
 * @name textarea
20019
 * @restrict E
20020
 *
20021
 * @description
20022
 * HTML textarea element control with angular data-binding. The data-binding and validation
20023
 * properties of this element are exactly the same as those of the
20024
 * {@link ng.directive:input input element}.
20025
 *
20026
 * @param {string} ngModel Assignable angular expression to data-bind to.
20027
 * @param {string=} name Property name of the form under which the control is published.
20028
 * @param {string=} required Sets `required` validation error key if the value is not entered.
20029
 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20030
 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20031
 *    `required` when you want to data-bind to the `required` attribute.
20032
 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20033
 *    minlength.
20034
 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20035
 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20036
 *    length.
20037
 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20038
 *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20039
 *    patterns defined as scope expressions.
20040
 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20041
 *    interaction with the input element.
20042
 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20043
 */
20044
 
20045
 
20046
/**
20047
 * @ngdoc directive
20048
 * @name input
20049
 * @restrict E
20050
 *
20051
 * @description
20052
 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
20053
 * input state control, and validation.
20054
 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
20055
 *
20056
 * <div class="alert alert-warning">
20057
 * **Note:** Not every feature offered is available for all input types.
20058
 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
20059
 * </div>
20060
 *
20061
 * @param {string} ngModel Assignable angular expression to data-bind to.
20062
 * @param {string=} name Property name of the form under which the control is published.
20063
 * @param {string=} required Sets `required` validation error key if the value is not entered.
20064
 * @param {boolean=} ngRequired Sets `required` attribute if set to true
20065
 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20066
 *    minlength.
20067
 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20068
 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20069
 *    length.
20070
 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20071
 *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20072
 *    patterns defined as scope expressions.
20073
 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20074
 *    interaction with the input element.
20075
 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20076
 *    This parameter is ignored for input[type=password] controls, which will never trim the
20077
 *    input.
20078
 *
20079
 * @example
20080
    <example name="input-directive" module="inputExample">
20081
      <file name="index.html">
20082
       <script>
20083
          angular.module('inputExample', [])
20084
            .controller('ExampleController', ['$scope', function($scope) {
20085
              $scope.user = {name: 'guest', last: 'visitor'};
20086
            }]);
20087
       </script>
20088
       <div ng-controller="ExampleController">
20089
         <form name="myForm">
20090
           User name: <input type="text" name="userName" ng-model="user.name" required>
20091
           <span class="error" ng-show="myForm.userName.$error.required">
20092
             Required!</span><br>
20093
           Last name: <input type="text" name="lastName" ng-model="user.last"
20094
             ng-minlength="3" ng-maxlength="10">
20095
           <span class="error" ng-show="myForm.lastName.$error.minlength">
20096
             Too short!</span>
20097
           <span class="error" ng-show="myForm.lastName.$error.maxlength">
20098
             Too long!</span><br>
20099
         </form>
20100
         <hr>
20101
         <tt>user = {{user}}</tt><br/>
20102
         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
20103
         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
20104
         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
20105
         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
20106
         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20107
         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20108
         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
20109
         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
20110
       </div>
20111
      </file>
20112
      <file name="protractor.js" type="protractor">
20113
        var user = element(by.exactBinding('user'));
20114
        var userNameValid = element(by.binding('myForm.userName.$valid'));
20115
        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
20116
        var lastNameError = element(by.binding('myForm.lastName.$error'));
20117
        var formValid = element(by.binding('myForm.$valid'));
20118
        var userNameInput = element(by.model('user.name'));
20119
        var userLastInput = element(by.model('user.last'));
20120
 
20121
        it('should initialize to model', function() {
20122
          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
20123
          expect(userNameValid.getText()).toContain('true');
20124
          expect(formValid.getText()).toContain('true');
20125
        });
20126
 
20127
        it('should be invalid if empty when required', function() {
20128
          userNameInput.clear();
20129
          userNameInput.sendKeys('');
20130
 
20131
          expect(user.getText()).toContain('{"last":"visitor"}');
20132
          expect(userNameValid.getText()).toContain('false');
20133
          expect(formValid.getText()).toContain('false');
20134
        });
20135
 
20136
        it('should be valid if empty when min length is set', function() {
20137
          userLastInput.clear();
20138
          userLastInput.sendKeys('');
20139
 
20140
          expect(user.getText()).toContain('{"name":"guest","last":""}');
20141
          expect(lastNameValid.getText()).toContain('true');
20142
          expect(formValid.getText()).toContain('true');
20143
        });
20144
 
20145
        it('should be invalid if less than required min length', function() {
20146
          userLastInput.clear();
20147
          userLastInput.sendKeys('xx');
20148
 
20149
          expect(user.getText()).toContain('{"name":"guest"}');
20150
          expect(lastNameValid.getText()).toContain('false');
20151
          expect(lastNameError.getText()).toContain('minlength');
20152
          expect(formValid.getText()).toContain('false');
20153
        });
20154
 
20155
        it('should be invalid if longer than max length', function() {
20156
          userLastInput.clear();
20157
          userLastInput.sendKeys('some ridiculously long name');
20158
 
20159
          expect(user.getText()).toContain('{"name":"guest"}');
20160
          expect(lastNameValid.getText()).toContain('false');
20161
          expect(lastNameError.getText()).toContain('maxlength');
20162
          expect(formValid.getText()).toContain('false');
20163
        });
20164
      </file>
20165
    </example>
20166
 */
20167
var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
20168
    function($browser, $sniffer, $filter, $parse) {
20169
  return {
20170
    restrict: 'E',
20171
    require: ['?ngModel'],
20172
    link: {
20173
      pre: function(scope, element, attr, ctrls) {
20174
        if (ctrls[0]) {
20175
          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
20176
                                                              $browser, $filter, $parse);
20177
        }
20178
      }
20179
    }
20180
  };
20181
}];
20182
 
20183
 
20184
 
20185
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
20186
/**
20187
 * @ngdoc directive
20188
 * @name ngValue
20189
 *
20190
 * @description
20191
 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
20192
 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
20193
 * the bound value.
20194
 *
20195
 * `ngValue` is useful when dynamically generating lists of radio buttons using
20196
 * {@link ngRepeat `ngRepeat`}, as shown below.
20197
 *
20198
 * Likewise, `ngValue` can be used to generate `<option>` elements for
20199
 * the {@link select `select`} element. In that case however, only strings are supported
20200
 * for the `value `attribute, so the resulting `ngModel` will always be a string.
20201
 * Support for `select` models with non-string values is available via `ngOptions`.
20202
 *
20203
 * @element input
20204
 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
20205
 *   of the `input` element
20206
 *
20207
 * @example
20208
    <example name="ngValue-directive" module="valueExample">
20209
      <file name="index.html">
20210
       <script>
20211
          angular.module('valueExample', [])
20212
            .controller('ExampleController', ['$scope', function($scope) {
20213
              $scope.names = ['pizza', 'unicorns', 'robots'];
20214
              $scope.my = { favorite: 'unicorns' };
20215
            }]);
20216
       </script>
20217
        <form ng-controller="ExampleController">
20218
          <h2>Which is your favorite?</h2>
20219
            <label ng-repeat="name in names" for="{{name}}">
20220
              {{name}}
20221
              <input type="radio"
20222
                     ng-model="my.favorite"
20223
                     ng-value="name"
20224
                     id="{{name}}"
20225
                     name="favorite">
20226
            </label>
20227
          <div>You chose {{my.favorite}}</div>
20228
        </form>
20229
      </file>
20230
      <file name="protractor.js" type="protractor">
20231
        var favorite = element(by.binding('my.favorite'));
20232
 
20233
        it('should initialize to model', function() {
20234
          expect(favorite.getText()).toContain('unicorns');
20235
        });
20236
        it('should bind the values to the inputs', function() {
20237
          element.all(by.model('my.favorite')).get(0).click();
20238
          expect(favorite.getText()).toContain('pizza');
20239
        });
20240
      </file>
20241
    </example>
20242
 */
20243
var ngValueDirective = function() {
20244
  return {
20245
    restrict: 'A',
20246
    priority: 100,
20247
    compile: function(tpl, tplAttr) {
20248
      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
20249
        return function ngValueConstantLink(scope, elm, attr) {
20250
          attr.$set('value', scope.$eval(attr.ngValue));
20251
        };
20252
      } else {
20253
        return function ngValueLink(scope, elm, attr) {
20254
          scope.$watch(attr.ngValue, function valueWatchAction(value) {
20255
            attr.$set('value', value);
20256
          });
20257
        };
20258
      }
20259
    }
20260
  };
20261
};
20262
 
20263
/**
20264
 * @ngdoc directive
20265
 * @name ngBind
20266
 * @restrict AC
20267
 *
20268
 * @description
20269
 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
20270
 * with the value of a given expression, and to update the text content when the value of that
20271
 * expression changes.
20272
 *
20273
 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
20274
 * `{{ expression }}` which is similar but less verbose.
20275
 *
20276
 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
20277
 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
20278
 * element attribute, it makes the bindings invisible to the user while the page is loading.
20279
 *
20280
 * An alternative solution to this problem would be using the
20281
 * {@link ng.directive:ngCloak ngCloak} directive.
20282
 *
20283
 *
20284
 * @element ANY
20285
 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
20286
 *
20287
 * @example
20288
 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
20289
   <example module="bindExample">
20290
     <file name="index.html">
20291
       <script>
20292
         angular.module('bindExample', [])
20293
           .controller('ExampleController', ['$scope', function($scope) {
20294
             $scope.name = 'Whirled';
20295
           }]);
20296
       </script>
20297
       <div ng-controller="ExampleController">
20298
         Enter name: <input type="text" ng-model="name"><br>
20299
         Hello <span ng-bind="name"></span>!
20300
       </div>
20301
     </file>
20302
     <file name="protractor.js" type="protractor">
20303
       it('should check ng-bind', function() {
20304
         var nameInput = element(by.model('name'));
20305
 
20306
         expect(element(by.binding('name')).getText()).toBe('Whirled');
20307
         nameInput.clear();
20308
         nameInput.sendKeys('world');
20309
         expect(element(by.binding('name')).getText()).toBe('world');
20310
       });
20311
     </file>
20312
   </example>
20313
 */
20314
var ngBindDirective = ['$compile', function($compile) {
20315
  return {
20316
    restrict: 'AC',
20317
    compile: function ngBindCompile(templateElement) {
20318
      $compile.$$addBindingClass(templateElement);
20319
      return function ngBindLink(scope, element, attr) {
20320
        $compile.$$addBindingInfo(element, attr.ngBind);
20321
        element = element[0];
20322
        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
20323
          element.textContent = value === undefined ? '' : value;
20324
        });
20325
      };
20326
    }
20327
  };
20328
}];
20329
 
20330
 
20331
/**
20332
 * @ngdoc directive
20333
 * @name ngBindTemplate
20334
 *
20335
 * @description
20336
 * The `ngBindTemplate` directive specifies that the element
20337
 * text content should be replaced with the interpolation of the template
20338
 * in the `ngBindTemplate` attribute.
20339
 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
20340
 * expressions. This directive is needed since some HTML elements
20341
 * (such as TITLE and OPTION) cannot contain SPAN elements.
20342
 *
20343
 * @element ANY
20344
 * @param {string} ngBindTemplate template of form
20345
 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
20346
 *
20347
 * @example
20348
 * Try it here: enter text in text box and watch the greeting change.
20349
   <example module="bindExample">
20350
     <file name="index.html">
20351
       <script>
20352
         angular.module('bindExample', [])
20353
           .controller('ExampleController', ['$scope', function($scope) {
20354
             $scope.salutation = 'Hello';
20355
             $scope.name = 'World';
20356
           }]);
20357
       </script>
20358
       <div ng-controller="ExampleController">
20359
        Salutation: <input type="text" ng-model="salutation"><br>
20360
        Name: <input type="text" ng-model="name"><br>
20361
        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
20362
       </div>
20363
     </file>
20364
     <file name="protractor.js" type="protractor">
20365
       it('should check ng-bind', function() {
20366
         var salutationElem = element(by.binding('salutation'));
20367
         var salutationInput = element(by.model('salutation'));
20368
         var nameInput = element(by.model('name'));
20369
 
20370
         expect(salutationElem.getText()).toBe('Hello World!');
20371
 
20372
         salutationInput.clear();
20373
         salutationInput.sendKeys('Greetings');
20374
         nameInput.clear();
20375
         nameInput.sendKeys('user');
20376
 
20377
         expect(salutationElem.getText()).toBe('Greetings user!');
20378
       });
20379
     </file>
20380
   </example>
20381
 */
20382
var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
20383
  return {
20384
    compile: function ngBindTemplateCompile(templateElement) {
20385
      $compile.$$addBindingClass(templateElement);
20386
      return function ngBindTemplateLink(scope, element, attr) {
20387
        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
20388
        $compile.$$addBindingInfo(element, interpolateFn.expressions);
20389
        element = element[0];
20390
        attr.$observe('ngBindTemplate', function(value) {
20391
          element.textContent = value === undefined ? '' : value;
20392
        });
20393
      };
20394
    }
20395
  };
20396
}];
20397
 
20398
 
20399
/**
20400
 * @ngdoc directive
20401
 * @name ngBindHtml
20402
 *
20403
 * @description
20404
 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
20405
 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
20406
 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
20407
 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
20408
 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
20409
 *
20410
 * You may also bypass sanitization for values you know are safe. To do so, bind to
20411
 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
20412
 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
20413
 *
20414
 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
20415
 * will have an exception (instead of an exploit.)
20416
 *
20417
 * @element ANY
20418
 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
20419
 *
20420
 * @example
20421
 
20422
   <example module="bindHtmlExample" deps="angular-sanitize.js">
20423
     <file name="index.html">
20424
       <div ng-controller="ExampleController">
20425
        <p ng-bind-html="myHTML"></p>
20426
       </div>
20427
     </file>
20428
 
20429
     <file name="script.js">
20430
       angular.module('bindHtmlExample', ['ngSanitize'])
20431
         .controller('ExampleController', ['$scope', function($scope) {
20432
           $scope.myHTML =
20433
              'I am an <code>HTML</code>string with ' +
20434
              '<a href="#">links!</a> and other <em>stuff</em>';
20435
         }]);
20436
     </file>
20437
 
20438
     <file name="protractor.js" type="protractor">
20439
       it('should check ng-bind-html', function() {
20440
         expect(element(by.binding('myHTML')).getText()).toBe(
20441
             'I am an HTMLstring with links! and other stuff');
20442
       });
20443
     </file>
20444
   </example>
20445
 */
20446
var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
20447
  return {
20448
    restrict: 'A',
20449
    compile: function ngBindHtmlCompile(tElement, tAttrs) {
20450
      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
20451
      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
20452
        return (value || '').toString();
20453
      });
20454
      $compile.$$addBindingClass(tElement);
20455
 
20456
      return function ngBindHtmlLink(scope, element, attr) {
20457
        $compile.$$addBindingInfo(element, attr.ngBindHtml);
20458
 
20459
        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
20460
          // we re-evaluate the expr because we want a TrustedValueHolderType
20461
          // for $sce, not a string
20462
          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
20463
        });
20464
      };
20465
    }
20466
  };
20467
}];
20468
 
20469
/**
20470
 * @ngdoc directive
20471
 * @name ngChange
20472
 *
20473
 * @description
20474
 * Evaluate the given expression when the user changes the input.
20475
 * The expression is evaluated immediately, unlike the JavaScript onchange event
20476
 * which only triggers at the end of a change (usually, when the user leaves the
20477
 * form element or presses the return key).
20478
 *
20479
 * The `ngChange` expression is only evaluated when a change in the input value causes
20480
 * a new value to be committed to the model.
20481
 *
20482
 * It will not be evaluated:
20483
 * * if the value returned from the `$parsers` transformation pipeline has not changed
20484
 * * if the input has continued to be invalid since the model will stay `null`
20485
 * * if the model is changed programmatically and not by a change to the input value
20486
 *
20487
 *
20488
 * Note, this directive requires `ngModel` to be present.
20489
 *
20490
 * @element input
20491
 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
20492
 * in input value.
20493
 *
20494
 * @example
20495
 * <example name="ngChange-directive" module="changeExample">
20496
 *   <file name="index.html">
20497
 *     <script>
20498
 *       angular.module('changeExample', [])
20499
 *         .controller('ExampleController', ['$scope', function($scope) {
20500
 *           $scope.counter = 0;
20501
 *           $scope.change = function() {
20502
 *             $scope.counter++;
20503
 *           };
20504
 *         }]);
20505
 *     </script>
20506
 *     <div ng-controller="ExampleController">
20507
 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
20508
 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
20509
 *       <label for="ng-change-example2">Confirmed</label><br />
20510
 *       <tt>debug = {{confirmed}}</tt><br/>
20511
 *       <tt>counter = {{counter}}</tt><br/>
20512
 *     </div>
20513
 *   </file>
20514
 *   <file name="protractor.js" type="protractor">
20515
 *     var counter = element(by.binding('counter'));
20516
 *     var debug = element(by.binding('confirmed'));
20517
 *
20518
 *     it('should evaluate the expression if changing from view', function() {
20519
 *       expect(counter.getText()).toContain('0');
20520
 *
20521
 *       element(by.id('ng-change-example1')).click();
20522
 *
20523
 *       expect(counter.getText()).toContain('1');
20524
 *       expect(debug.getText()).toContain('true');
20525
 *     });
20526
 *
20527
 *     it('should not evaluate the expression if changing from model', function() {
20528
 *       element(by.id('ng-change-example2')).click();
20529
 
20530
 *       expect(counter.getText()).toContain('0');
20531
 *       expect(debug.getText()).toContain('true');
20532
 *     });
20533
 *   </file>
20534
 * </example>
20535
 */
20536
var ngChangeDirective = valueFn({
20537
  restrict: 'A',
20538
  require: 'ngModel',
20539
  link: function(scope, element, attr, ctrl) {
20540
    ctrl.$viewChangeListeners.push(function() {
20541
      scope.$eval(attr.ngChange);
20542
    });
20543
  }
20544
});
20545
 
20546
function classDirective(name, selector) {
20547
  name = 'ngClass' + name;
20548
  return ['$animate', function($animate) {
20549
    return {
20550
      restrict: 'AC',
20551
      link: function(scope, element, attr) {
20552
        var oldVal;
20553
 
20554
        scope.$watch(attr[name], ngClassWatchAction, true);
20555
 
20556
        attr.$observe('class', function(value) {
20557
          ngClassWatchAction(scope.$eval(attr[name]));
20558
        });
20559
 
20560
 
20561
        if (name !== 'ngClass') {
20562
          scope.$watch('$index', function($index, old$index) {
20563
            // jshint bitwise: false
20564
            var mod = $index & 1;
20565
            if (mod !== (old$index & 1)) {
20566
              var classes = arrayClasses(scope.$eval(attr[name]));
20567
              mod === selector ?
20568
                addClasses(classes) :
20569
                removeClasses(classes);
20570
            }
20571
          });
20572
        }
20573
 
20574
        function addClasses(classes) {
20575
          var newClasses = digestClassCounts(classes, 1);
20576
          attr.$addClass(newClasses);
20577
        }
20578
 
20579
        function removeClasses(classes) {
20580
          var newClasses = digestClassCounts(classes, -1);
20581
          attr.$removeClass(newClasses);
20582
        }
20583
 
20584
        function digestClassCounts(classes, count) {
20585
          var classCounts = element.data('$classCounts') || {};
20586
          var classesToUpdate = [];
20587
          forEach(classes, function(className) {
20588
            if (count > 0 || classCounts[className]) {
20589
              classCounts[className] = (classCounts[className] || 0) + count;
20590
              if (classCounts[className] === +(count > 0)) {
20591
                classesToUpdate.push(className);
20592
              }
20593
            }
20594
          });
20595
          element.data('$classCounts', classCounts);
20596
          return classesToUpdate.join(' ');
20597
        }
20598
 
20599
        function updateClasses(oldClasses, newClasses) {
20600
          var toAdd = arrayDifference(newClasses, oldClasses);
20601
          var toRemove = arrayDifference(oldClasses, newClasses);
20602
          toAdd = digestClassCounts(toAdd, 1);
20603
          toRemove = digestClassCounts(toRemove, -1);
20604
          if (toAdd && toAdd.length) {
20605
            $animate.addClass(element, toAdd);
20606
          }
20607
          if (toRemove && toRemove.length) {
20608
            $animate.removeClass(element, toRemove);
20609
          }
20610
        }
20611
 
20612
        function ngClassWatchAction(newVal) {
20613
          if (selector === true || scope.$index % 2 === selector) {
20614
            var newClasses = arrayClasses(newVal || []);
20615
            if (!oldVal) {
20616
              addClasses(newClasses);
20617
            } else if (!equals(newVal,oldVal)) {
20618
              var oldClasses = arrayClasses(oldVal);
20619
              updateClasses(oldClasses, newClasses);
20620
            }
20621
          }
20622
          oldVal = shallowCopy(newVal);
20623
        }
20624
      }
20625
    };
20626
 
20627
    function arrayDifference(tokens1, tokens2) {
20628
      var values = [];
20629
 
20630
      outer:
20631
      for (var i = 0; i < tokens1.length; i++) {
20632
        var token = tokens1[i];
20633
        for (var j = 0; j < tokens2.length; j++) {
20634
          if (token == tokens2[j]) continue outer;
20635
        }
20636
        values.push(token);
20637
      }
20638
      return values;
20639
    }
20640
 
20641
    function arrayClasses(classVal) {
20642
      if (isArray(classVal)) {
20643
        return classVal;
20644
      } else if (isString(classVal)) {
20645
        return classVal.split(' ');
20646
      } else if (isObject(classVal)) {
20647
        var classes = [];
20648
        forEach(classVal, function(v, k) {
20649
          if (v) {
20650
            classes = classes.concat(k.split(' '));
20651
          }
20652
        });
20653
        return classes;
20654
      }
20655
      return classVal;
20656
    }
20657
  }];
20658
}
20659
 
20660
/**
20661
 * @ngdoc directive
20662
 * @name ngClass
20663
 * @restrict AC
20664
 *
20665
 * @description
20666
 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
20667
 * an expression that represents all classes to be added.
20668
 *
20669
 * The directive operates in three different ways, depending on which of three types the expression
20670
 * evaluates to:
20671
 *
20672
 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
20673
 * names.
20674
 *
20675
 * 2. If the expression evaluates to an array, each element of the array should be a string that is
20676
 * one or more space-delimited class names.
20677
 *
20678
 * 3. If the expression evaluates to an object, then for each key-value pair of the
20679
 * object with a truthy value the corresponding key is used as a class name.
20680
 *
20681
 * The directive won't add duplicate classes if a particular class was already set.
20682
 *
20683
 * When the expression changes, the previously added classes are removed and only then the
20684
 * new classes are added.
20685
 *
20686
 * @animations
20687
 * **add** - happens just before the class is applied to the elements
20688
 *
20689
 * **remove** - happens just before the class is removed from the element
20690
 *
20691
 * @element ANY
20692
 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
20693
 *   of the evaluation can be a string representing space delimited class
20694
 *   names, an array, or a map of class names to boolean values. In the case of a map, the
20695
 *   names of the properties whose values are truthy will be added as css classes to the
20696
 *   element.
20697
 *
20698
 * @example Example that demonstrates basic bindings via ngClass directive.
20699
   <example>
20700
     <file name="index.html">
20701
       <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
20702
       <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
20703
       <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
20704
       <input type="checkbox" ng-model="error"> error (apply "red" class)
20705
       <hr>
20706
       <p ng-class="style">Using String Syntax</p>
20707
       <input type="text" ng-model="style" placeholder="Type: bold strike red">
20708
       <hr>
20709
       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
20710
       <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
20711
       <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
20712
       <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
20713
     </file>
20714
     <file name="style.css">
20715
       .strike {
20716
         text-decoration: line-through;
20717
       }
20718
       .bold {
20719
           font-weight: bold;
20720
       }
20721
       .red {
20722
           color: red;
20723
       }
20724
     </file>
20725
     <file name="protractor.js" type="protractor">
20726
       var ps = element.all(by.css('p'));
20727
 
20728
       it('should let you toggle the class', function() {
20729
 
20730
         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
20731
         expect(ps.first().getAttribute('class')).not.toMatch(/red/);
20732
 
20733
         element(by.model('important')).click();
20734
         expect(ps.first().getAttribute('class')).toMatch(/bold/);
20735
 
20736
         element(by.model('error')).click();
20737
         expect(ps.first().getAttribute('class')).toMatch(/red/);
20738
       });
20739
 
20740
       it('should let you toggle string example', function() {
20741
         expect(ps.get(1).getAttribute('class')).toBe('');
20742
         element(by.model('style')).clear();
20743
         element(by.model('style')).sendKeys('red');
20744
         expect(ps.get(1).getAttribute('class')).toBe('red');
20745
       });
20746
 
20747
       it('array example should have 3 classes', function() {
20748
         expect(ps.last().getAttribute('class')).toBe('');
20749
         element(by.model('style1')).sendKeys('bold');
20750
         element(by.model('style2')).sendKeys('strike');
20751
         element(by.model('style3')).sendKeys('red');
20752
         expect(ps.last().getAttribute('class')).toBe('bold strike red');
20753
       });
20754
     </file>
20755
   </example>
20756
 
20757
   ## Animations
20758
 
20759
   The example below demonstrates how to perform animations using ngClass.
20760
 
20761
   <example module="ngAnimate" deps="angular-animate.js" animations="true">
20762
     <file name="index.html">
20763
      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
20764
      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
20765
      <br>
20766
      <span class="base-class" ng-class="myVar">Sample Text</span>
20767
     </file>
20768
     <file name="style.css">
20769
       .base-class {
20770
         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
20771
         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
20772
       }
20773
 
20774
       .base-class.my-class {
20775
         color: red;
20776
         font-size:3em;
20777
       }
20778
     </file>
20779
     <file name="protractor.js" type="protractor">
20780
       it('should check ng-class', function() {
20781
         expect(element(by.css('.base-class')).getAttribute('class')).not.
20782
           toMatch(/my-class/);
20783
 
20784
         element(by.id('setbtn')).click();
20785
 
20786
         expect(element(by.css('.base-class')).getAttribute('class')).
20787
           toMatch(/my-class/);
20788
 
20789
         element(by.id('clearbtn')).click();
20790
 
20791
         expect(element(by.css('.base-class')).getAttribute('class')).not.
20792
           toMatch(/my-class/);
20793
       });
20794
     </file>
20795
   </example>
20796
 
20797
 
20798
   ## ngClass and pre-existing CSS3 Transitions/Animations
20799
   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
20800
   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
20801
   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
20802
   to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and
20803
   {@link ng.$animate#removeClass $animate.removeClass}.
20804
 */
20805
var ngClassDirective = classDirective('', true);
20806
 
20807
/**
20808
 * @ngdoc directive
20809
 * @name ngClassOdd
20810
 * @restrict AC
20811
 *
20812
 * @description
20813
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
20814
 * {@link ng.directive:ngClass ngClass}, except they work in
20815
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
20816
 *
20817
 * This directive can be applied only within the scope of an
20818
 * {@link ng.directive:ngRepeat ngRepeat}.
20819
 *
20820
 * @element ANY
20821
 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
20822
 *   of the evaluation can be a string representing space delimited class names or an array.
20823
 *
20824
 * @example
20825
   <example>
20826
     <file name="index.html">
20827
        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
20828
          <li ng-repeat="name in names">
20829
           <span ng-class-odd="'odd'" ng-class-even="'even'">
20830
             {{name}}
20831
           </span>
20832
          </li>
20833
        </ol>
20834
     </file>
20835
     <file name="style.css">
20836
       .odd {
20837
         color: red;
20838
       }
20839
       .even {
20840
         color: blue;
20841
       }
20842
     </file>
20843
     <file name="protractor.js" type="protractor">
20844
       it('should check ng-class-odd and ng-class-even', function() {
20845
         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
20846
           toMatch(/odd/);
20847
         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
20848
           toMatch(/even/);
20849
       });
20850
     </file>
20851
   </example>
20852
 */
20853
var ngClassOddDirective = classDirective('Odd', 0);
20854
 
20855
/**
20856
 * @ngdoc directive
20857
 * @name ngClassEven
20858
 * @restrict AC
20859
 *
20860
 * @description
20861
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
20862
 * {@link ng.directive:ngClass ngClass}, except they work in
20863
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
20864
 *
20865
 * This directive can be applied only within the scope of an
20866
 * {@link ng.directive:ngRepeat ngRepeat}.
20867
 *
20868
 * @element ANY
20869
 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
20870
 *   result of the evaluation can be a string representing space delimited class names or an array.
20871
 *
20872
 * @example
20873
   <example>
20874
     <file name="index.html">
20875
        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
20876
          <li ng-repeat="name in names">
20877
           <span ng-class-odd="'odd'" ng-class-even="'even'">
20878
             {{name}} &nbsp; &nbsp; &nbsp;
20879
           </span>
20880
          </li>
20881
        </ol>
20882
     </file>
20883
     <file name="style.css">
20884
       .odd {
20885
         color: red;
20886
       }
20887
       .even {
20888
         color: blue;
20889
       }
20890
     </file>
20891
     <file name="protractor.js" type="protractor">
20892
       it('should check ng-class-odd and ng-class-even', function() {
20893
         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
20894
           toMatch(/odd/);
20895
         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
20896
           toMatch(/even/);
20897
       });
20898
     </file>
20899
   </example>
20900
 */
20901
var ngClassEvenDirective = classDirective('Even', 1);
20902
 
20903
/**
20904
 * @ngdoc directive
20905
 * @name ngCloak
20906
 * @restrict AC
20907
 *
20908
 * @description
20909
 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
20910
 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
20911
 * directive to avoid the undesirable flicker effect caused by the html template display.
20912
 *
20913
 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
20914
 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
20915
 * of the browser view.
20916
 *
20917
 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
20918
 * `angular.min.js`.
20919
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
20920
 *
20921
 * ```css
20922
 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
20923
 *   display: none !important;
20924
 * }
20925
 * ```
20926
 *
20927
 * When this css rule is loaded by the browser, all html elements (including their children) that
20928
 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
20929
 * during the compilation of the template it deletes the `ngCloak` element attribute, making
20930
 * the compiled element visible.
20931
 *
20932
 * For the best result, the `angular.js` script must be loaded in the head section of the html
20933
 * document; alternatively, the css rule above must be included in the external stylesheet of the
20934
 * application.
20935
 *
20936
 * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
20937
 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
20938
 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
20939
 *
20940
 * @element ANY
20941
 *
20942
 * @example
20943
   <example>
20944
     <file name="index.html">
20945
        <div id="template1" ng-cloak>{{ 'hello' }}</div>
20946
        <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
20947
     </file>
20948
     <file name="protractor.js" type="protractor">
20949
       it('should remove the template directive and css class', function() {
20950
         expect($('#template1').getAttribute('ng-cloak')).
20951
           toBeNull();
20952
         expect($('#template2').getAttribute('ng-cloak')).
20953
           toBeNull();
20954
       });
20955
     </file>
20956
   </example>
20957
 *
20958
 */
20959
var ngCloakDirective = ngDirective({
20960
  compile: function(element, attr) {
20961
    attr.$set('ngCloak', undefined);
20962
    element.removeClass('ng-cloak');
20963
  }
20964
});
20965
 
20966
/**
20967
 * @ngdoc directive
20968
 * @name ngController
20969
 *
20970
 * @description
20971
 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
20972
 * supports the principles behind the Model-View-Controller design pattern.
20973
 *
20974
 * MVC components in angular:
20975
 *
20976
 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
20977
 *   are accessed through bindings.
20978
 * * View — The template (HTML with data bindings) that is rendered into the View.
20979
 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
20980
 *   logic behind the application to decorate the scope with functions and values
20981
 *
20982
 * Note that you can also attach controllers to the DOM by declaring it in a route definition
20983
 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
20984
 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
20985
 * and executed twice.
20986
 *
20987
 * @element ANY
20988
 * @scope
20989
 * @priority 500
20990
 * @param {expression} ngController Name of a constructor function registered with the current
20991
 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
20992
 * that on the current scope evaluates to a constructor function.
20993
 *
20994
 * The controller instance can be published into a scope property by specifying
20995
 * `ng-controller="as propertyName"`.
20996
 *
20997
 * If the current `$controllerProvider` is configured to use globals (via
20998
 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
20999
 * also be the name of a globally accessible constructor function (not recommended).
21000
 *
21001
 * @example
21002
 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
21003
 * greeting are methods declared on the controller (see source tab). These methods can
21004
 * easily be called from the angular markup. Any changes to the data are automatically reflected
21005
 * in the View without the need for a manual update.
21006
 *
21007
 * Two different declaration styles are included below:
21008
 *
21009
 * * one binds methods and properties directly onto the controller using `this`:
21010
 * `ng-controller="SettingsController1 as settings"`
21011
 * * one injects `$scope` into the controller:
21012
 * `ng-controller="SettingsController2"`
21013
 *
21014
 * The second option is more common in the Angular community, and is generally used in boilerplates
21015
 * and in this guide. However, there are advantages to binding properties directly to the controller
21016
 * and avoiding scope.
21017
 *
21018
 * * Using `controller as` makes it obvious which controller you are accessing in the template when
21019
 * multiple controllers apply to an element.
21020
 * * If you are writing your controllers as classes you have easier access to the properties and
21021
 * methods, which will appear on the scope, from inside the controller code.
21022
 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
21023
 * inheritance masking primitives.
21024
 *
21025
 * This example demonstrates the `controller as` syntax.
21026
 *
21027
 * <example name="ngControllerAs" module="controllerAsExample">
21028
 *   <file name="index.html">
21029
 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
21030
 *      Name: <input type="text" ng-model="settings.name"/>
21031
 *      [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
21032
 *      Contact:
21033
 *      <ul>
21034
 *        <li ng-repeat="contact in settings.contacts">
21035
 *          <select ng-model="contact.type">
21036
 *             <option>phone</option>
21037
 *             <option>email</option>
21038
 *          </select>
21039
 *          <input type="text" ng-model="contact.value"/>
21040
 *          [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
21041
 *          | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
21042
 *        </li>
21043
 *        <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
21044
 *     </ul>
21045
 *    </div>
21046
 *   </file>
21047
 *   <file name="app.js">
21048
 *    angular.module('controllerAsExample', [])
21049
 *      .controller('SettingsController1', SettingsController1);
21050
 *
21051
 *    function SettingsController1() {
21052
 *      this.name = "John Smith";
21053
 *      this.contacts = [
21054
 *        {type: 'phone', value: '408 555 1212'},
21055
 *        {type: 'email', value: 'john.smith@example.org'} ];
21056
 *    }
21057
 *
21058
 *    SettingsController1.prototype.greet = function() {
21059
 *      alert(this.name);
21060
 *    };
21061
 *
21062
 *    SettingsController1.prototype.addContact = function() {
21063
 *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
21064
 *    };
21065
 *
21066
 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
21067
 *     var index = this.contacts.indexOf(contactToRemove);
21068
 *      this.contacts.splice(index, 1);
21069
 *    };
21070
 *
21071
 *    SettingsController1.prototype.clearContact = function(contact) {
21072
 *      contact.type = 'phone';
21073
 *      contact.value = '';
21074
 *    };
21075
 *   </file>
21076
 *   <file name="protractor.js" type="protractor">
21077
 *     it('should check controller as', function() {
21078
 *       var container = element(by.id('ctrl-as-exmpl'));
21079
 *         expect(container.element(by.model('settings.name'))
21080
 *           .getAttribute('value')).toBe('John Smith');
21081
 *
21082
 *       var firstRepeat =
21083
 *           container.element(by.repeater('contact in settings.contacts').row(0));
21084
 *       var secondRepeat =
21085
 *           container.element(by.repeater('contact in settings.contacts').row(1));
21086
 *
21087
 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21088
 *           .toBe('408 555 1212');
21089
 *
21090
 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21091
 *           .toBe('john.smith@example.org');
21092
 *
21093
 *       firstRepeat.element(by.linkText('clear')).click();
21094
 *
21095
 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21096
 *           .toBe('');
21097
 *
21098
 *       container.element(by.linkText('add')).click();
21099
 *
21100
 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
21101
 *           .element(by.model('contact.value'))
21102
 *           .getAttribute('value'))
21103
 *           .toBe('yourname@example.org');
21104
 *     });
21105
 *   </file>
21106
 * </example>
21107
 *
21108
 * This example demonstrates the "attach to `$scope`" style of controller.
21109
 *
21110
 * <example name="ngController" module="controllerExample">
21111
 *  <file name="index.html">
21112
 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
21113
 *     Name: <input type="text" ng-model="name"/>
21114
 *     [ <a href="" ng-click="greet()">greet</a> ]<br/>
21115
 *     Contact:
21116
 *     <ul>
21117
 *       <li ng-repeat="contact in contacts">
21118
 *         <select ng-model="contact.type">
21119
 *            <option>phone</option>
21120
 *            <option>email</option>
21121
 *         </select>
21122
 *         <input type="text" ng-model="contact.value"/>
21123
 *         [ <a href="" ng-click="clearContact(contact)">clear</a>
21124
 *         | <a href="" ng-click="removeContact(contact)">X</a> ]
21125
 *       </li>
21126
 *       <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
21127
 *    </ul>
21128
 *   </div>
21129
 *  </file>
21130
 *  <file name="app.js">
21131
 *   angular.module('controllerExample', [])
21132
 *     .controller('SettingsController2', ['$scope', SettingsController2]);
21133
 *
21134
 *   function SettingsController2($scope) {
21135
 *     $scope.name = "John Smith";
21136
 *     $scope.contacts = [
21137
 *       {type:'phone', value:'408 555 1212'},
21138
 *       {type:'email', value:'john.smith@example.org'} ];
21139
 *
21140
 *     $scope.greet = function() {
21141
 *       alert($scope.name);
21142
 *     };
21143
 *
21144
 *     $scope.addContact = function() {
21145
 *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
21146
 *     };
21147
 *
21148
 *     $scope.removeContact = function(contactToRemove) {
21149
 *       var index = $scope.contacts.indexOf(contactToRemove);
21150
 *       $scope.contacts.splice(index, 1);
21151
 *     };
21152
 *
21153
 *     $scope.clearContact = function(contact) {
21154
 *       contact.type = 'phone';
21155
 *       contact.value = '';
21156
 *     };
21157
 *   }
21158
 *  </file>
21159
 *  <file name="protractor.js" type="protractor">
21160
 *    it('should check controller', function() {
21161
 *      var container = element(by.id('ctrl-exmpl'));
21162
 *
21163
 *      expect(container.element(by.model('name'))
21164
 *          .getAttribute('value')).toBe('John Smith');
21165
 *
21166
 *      var firstRepeat =
21167
 *          container.element(by.repeater('contact in contacts').row(0));
21168
 *      var secondRepeat =
21169
 *          container.element(by.repeater('contact in contacts').row(1));
21170
 *
21171
 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21172
 *          .toBe('408 555 1212');
21173
 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21174
 *          .toBe('john.smith@example.org');
21175
 *
21176
 *      firstRepeat.element(by.linkText('clear')).click();
21177
 *
21178
 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21179
 *          .toBe('');
21180
 *
21181
 *      container.element(by.linkText('add')).click();
21182
 *
21183
 *      expect(container.element(by.repeater('contact in contacts').row(2))
21184
 *          .element(by.model('contact.value'))
21185
 *          .getAttribute('value'))
21186
 *          .toBe('yourname@example.org');
21187
 *    });
21188
 *  </file>
21189
 *</example>
21190
 
21191
 */
21192
var ngControllerDirective = [function() {
21193
  return {
21194
    restrict: 'A',
21195
    scope: true,
21196
    controller: '@',
21197
    priority: 500
21198
  };
21199
}];
21200
 
21201
/**
21202
 * @ngdoc directive
21203
 * @name ngCsp
21204
 *
21205
 * @element html
21206
 * @description
21207
 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
21208
 *
21209
 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
21210
 *
21211
 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
21212
 * For Angular to be CSP compatible there are only two things that we need to do differently:
21213
 *
21214
 * - don't use `Function` constructor to generate optimized value getters
21215
 * - don't inject custom stylesheet into the document
21216
 *
21217
 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
21218
 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
21219
 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
21220
 * be raised.
21221
 *
21222
 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
21223
 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
21224
 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
21225
 *
21226
 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
21227
 * autodetection however triggers a CSP error to be logged in the console:
21228
 *
21229
 * ```
21230
 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
21231
 * script in the following Content Security Policy directive: "default-src 'self'". Note that
21232
 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
21233
 * ```
21234
 *
21235
 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
21236
 * directive on the root element of the application or on the `angular.js` script tag, whichever
21237
 * appears first in the html document.
21238
 *
21239
 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
21240
 *
21241
 * @example
21242
 * This example shows how to apply the `ngCsp` directive to the `html` tag.
21243
   ```html
21244
     <!doctype html>
21245
     <html ng-app ng-csp>
21246
     ...
21247
     ...
21248
     </html>
21249
   ```
21250
  * @example
21251
      // Note: the suffix `.csp` in the example name triggers
21252
      // csp mode in our http server!
21253
      <example name="example.csp" module="cspExample" ng-csp="true">
21254
        <file name="index.html">
21255
          <div ng-controller="MainController as ctrl">
21256
            <div>
21257
              <button ng-click="ctrl.inc()" id="inc">Increment</button>
21258
              <span id="counter">
21259
                {{ctrl.counter}}
21260
              </span>
21261
            </div>
21262
 
21263
            <div>
21264
              <button ng-click="ctrl.evil()" id="evil">Evil</button>
21265
              <span id="evilError">
21266
                {{ctrl.evilError}}
21267
              </span>
21268
            </div>
21269
          </div>
21270
        </file>
21271
        <file name="script.js">
21272
           angular.module('cspExample', [])
21273
             .controller('MainController', function() {
21274
                this.counter = 0;
21275
                this.inc = function() {
21276
                  this.counter++;
21277
                };
21278
                this.evil = function() {
21279
                  // jshint evil:true
21280
                  try {
21281
                    eval('1+2');
21282
                  } catch (e) {
21283
                    this.evilError = e.message;
21284
                  }
21285
                };
21286
              });
21287
        </file>
21288
        <file name="protractor.js" type="protractor">
21289
          var util, webdriver;
21290
 
21291
          var incBtn = element(by.id('inc'));
21292
          var counter = element(by.id('counter'));
21293
          var evilBtn = element(by.id('evil'));
21294
          var evilError = element(by.id('evilError'));
21295
 
21296
          function getAndClearSevereErrors() {
21297
            return browser.manage().logs().get('browser').then(function(browserLog) {
21298
              return browserLog.filter(function(logEntry) {
21299
                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
21300
              });
21301
            });
21302
          }
21303
 
21304
          function clearErrors() {
21305
            getAndClearSevereErrors();
21306
          }
21307
 
21308
          function expectNoErrors() {
21309
            getAndClearSevereErrors().then(function(filteredLog) {
21310
              expect(filteredLog.length).toEqual(0);
21311
              if (filteredLog.length) {
21312
                console.log('browser console errors: ' + util.inspect(filteredLog));
21313
              }
21314
            });
21315
          }
21316
 
21317
          function expectError(regex) {
21318
            getAndClearSevereErrors().then(function(filteredLog) {
21319
              var found = false;
21320
              filteredLog.forEach(function(log) {
21321
                if (log.message.match(regex)) {
21322
                  found = true;
21323
                }
21324
              });
21325
              if (!found) {
21326
                throw new Error('expected an error that matches ' + regex);
21327
              }
21328
            });
21329
          }
21330
 
21331
          beforeEach(function() {
21332
            util = require('util');
21333
            webdriver = require('protractor/node_modules/selenium-webdriver');
21334
          });
21335
 
21336
          // For now, we only test on Chrome,
21337
          // as Safari does not load the page with Protractor's injected scripts,
21338
          // and Firefox webdriver always disables content security policy (#6358)
21339
          if (browser.params.browser !== 'chrome') {
21340
            return;
21341
          }
21342
 
21343
          it('should not report errors when the page is loaded', function() {
21344
            // clear errors so we are not dependent on previous tests
21345
            clearErrors();
21346
            // Need to reload the page as the page is already loaded when
21347
            // we come here
21348
            browser.driver.getCurrentUrl().then(function(url) {
21349
              browser.get(url);
21350
            });
21351
            expectNoErrors();
21352
          });
21353
 
21354
          it('should evaluate expressions', function() {
21355
            expect(counter.getText()).toEqual('0');
21356
            incBtn.click();
21357
            expect(counter.getText()).toEqual('1');
21358
            expectNoErrors();
21359
          });
21360
 
21361
          it('should throw and report an error when using "eval"', function() {
21362
            evilBtn.click();
21363
            expect(evilError.getText()).toMatch(/Content Security Policy/);
21364
            expectError(/Content Security Policy/);
21365
          });
21366
        </file>
21367
      </example>
21368
  */
21369
 
21370
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
21371
// bootstrap the system (before $parse is instantiated), for this reason we just have
21372
// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
21373
 
21374
/**
21375
 * @ngdoc directive
21376
 * @name ngClick
21377
 *
21378
 * @description
21379
 * The ngClick directive allows you to specify custom behavior when
21380
 * an element is clicked.
21381
 *
21382
 * @element ANY
21383
 * @priority 0
21384
 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
21385
 * click. ({@link guide/expression#-event- Event object is available as `$event`})
21386
 *
21387
 * @example
21388
   <example>
21389
     <file name="index.html">
21390
      <button ng-click="count = count + 1" ng-init="count=0">
21391
        Increment
21392
      </button>
21393
      <span>
21394
        count: {{count}}
21395
      </span>
21396
     </file>
21397
     <file name="protractor.js" type="protractor">
21398
       it('should check ng-click', function() {
21399
         expect(element(by.binding('count')).getText()).toMatch('0');
21400
         element(by.css('button')).click();
21401
         expect(element(by.binding('count')).getText()).toMatch('1');
21402
       });
21403
     </file>
21404
   </example>
21405
 */
21406
/*
21407
 * A collection of directives that allows creation of custom event handlers that are defined as
21408
 * angular expressions and are compiled and executed within the current scope.
21409
 */
21410
var ngEventDirectives = {};
21411
 
21412
// For events that might fire synchronously during DOM manipulation
21413
// we need to execute their event handlers asynchronously using $evalAsync,
21414
// so that they are not executed in an inconsistent state.
21415
var forceAsyncEvents = {
21416
  'blur': true,
21417
  'focus': true
21418
};
21419
forEach(
21420
  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
21421
  function(eventName) {
21422
    var directiveName = directiveNormalize('ng-' + eventName);
21423
    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
21424
      return {
21425
        restrict: 'A',
21426
        compile: function($element, attr) {
21427
          // We expose the powerful $event object on the scope that provides access to the Window,
21428
          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
21429
          // checks at the cost of speed since event handler expressions are not executed as
21430
          // frequently as regular change detection.
21431
          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
21432
          return function ngEventHandler(scope, element) {
21433
            element.on(eventName, function(event) {
21434
              var callback = function() {
21435
                fn(scope, {$event:event});
21436
              };
21437
              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
21438
                scope.$evalAsync(callback);
21439
              } else {
21440
                scope.$apply(callback);
21441
              }
21442
            });
21443
          };
21444
        }
21445
      };
21446
    }];
21447
  }
21448
);
21449
 
21450
/**
21451
 * @ngdoc directive
21452
 * @name ngDblclick
21453
 *
21454
 * @description
21455
 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
21456
 *
21457
 * @element ANY
21458
 * @priority 0
21459
 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
21460
 * a dblclick. (The Event object is available as `$event`)
21461
 *
21462
 * @example
21463
   <example>
21464
     <file name="index.html">
21465
      <button ng-dblclick="count = count + 1" ng-init="count=0">
21466
        Increment (on double click)
21467
      </button>
21468
      count: {{count}}
21469
     </file>
21470
   </example>
21471
 */
21472
 
21473
 
21474
/**
21475
 * @ngdoc directive
21476
 * @name ngMousedown
21477
 *
21478
 * @description
21479
 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
21480
 *
21481
 * @element ANY
21482
 * @priority 0
21483
 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
21484
 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
21485
 *
21486
 * @example
21487
   <example>
21488
     <file name="index.html">
21489
      <button ng-mousedown="count = count + 1" ng-init="count=0">
21490
        Increment (on mouse down)
21491
      </button>
21492
      count: {{count}}
21493
     </file>
21494
   </example>
21495
 */
21496
 
21497
 
21498
/**
21499
 * @ngdoc directive
21500
 * @name ngMouseup
21501
 *
21502
 * @description
21503
 * Specify custom behavior on mouseup event.
21504
 *
21505
 * @element ANY
21506
 * @priority 0
21507
 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
21508
 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
21509
 *
21510
 * @example
21511
   <example>
21512
     <file name="index.html">
21513
      <button ng-mouseup="count = count + 1" ng-init="count=0">
21514
        Increment (on mouse up)
21515
      </button>
21516
      count: {{count}}
21517
     </file>
21518
   </example>
21519
 */
21520
 
21521
/**
21522
 * @ngdoc directive
21523
 * @name ngMouseover
21524
 *
21525
 * @description
21526
 * Specify custom behavior on mouseover event.
21527
 *
21528
 * @element ANY
21529
 * @priority 0
21530
 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
21531
 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
21532
 *
21533
 * @example
21534
   <example>
21535
     <file name="index.html">
21536
      <button ng-mouseover="count = count + 1" ng-init="count=0">
21537
        Increment (when mouse is over)
21538
      </button>
21539
      count: {{count}}
21540
     </file>
21541
   </example>
21542
 */
21543
 
21544
 
21545
/**
21546
 * @ngdoc directive
21547
 * @name ngMouseenter
21548
 *
21549
 * @description
21550
 * Specify custom behavior on mouseenter event.
21551
 *
21552
 * @element ANY
21553
 * @priority 0
21554
 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
21555
 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
21556
 *
21557
 * @example
21558
   <example>
21559
     <file name="index.html">
21560
      <button ng-mouseenter="count = count + 1" ng-init="count=0">
21561
        Increment (when mouse enters)
21562
      </button>
21563
      count: {{count}}
21564
     </file>
21565
   </example>
21566
 */
21567
 
21568
 
21569
/**
21570
 * @ngdoc directive
21571
 * @name ngMouseleave
21572
 *
21573
 * @description
21574
 * Specify custom behavior on mouseleave event.
21575
 *
21576
 * @element ANY
21577
 * @priority 0
21578
 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
21579
 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
21580
 *
21581
 * @example
21582
   <example>
21583
     <file name="index.html">
21584
      <button ng-mouseleave="count = count + 1" ng-init="count=0">
21585
        Increment (when mouse leaves)
21586
      </button>
21587
      count: {{count}}
21588
     </file>
21589
   </example>
21590
 */
21591
 
21592
 
21593
/**
21594
 * @ngdoc directive
21595
 * @name ngMousemove
21596
 *
21597
 * @description
21598
 * Specify custom behavior on mousemove event.
21599
 *
21600
 * @element ANY
21601
 * @priority 0
21602
 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
21603
 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
21604
 *
21605
 * @example
21606
   <example>
21607
     <file name="index.html">
21608
      <button ng-mousemove="count = count + 1" ng-init="count=0">
21609
        Increment (when mouse moves)
21610
      </button>
21611
      count: {{count}}
21612
     </file>
21613
   </example>
21614
 */
21615
 
21616
 
21617
/**
21618
 * @ngdoc directive
21619
 * @name ngKeydown
21620
 *
21621
 * @description
21622
 * Specify custom behavior on keydown event.
21623
 *
21624
 * @element ANY
21625
 * @priority 0
21626
 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
21627
 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21628
 *
21629
 * @example
21630
   <example>
21631
     <file name="index.html">
21632
      <input ng-keydown="count = count + 1" ng-init="count=0">
21633
      key down count: {{count}}
21634
     </file>
21635
   </example>
21636
 */
21637
 
21638
 
21639
/**
21640
 * @ngdoc directive
21641
 * @name ngKeyup
21642
 *
21643
 * @description
21644
 * Specify custom behavior on keyup event.
21645
 *
21646
 * @element ANY
21647
 * @priority 0
21648
 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
21649
 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21650
 *
21651
 * @example
21652
   <example>
21653
     <file name="index.html">
21654
       <p>Typing in the input box below updates the key count</p>
21655
       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
21656
 
21657
       <p>Typing in the input box below updates the keycode</p>
21658
       <input ng-keyup="event=$event">
21659
       <p>event keyCode: {{ event.keyCode }}</p>
21660
       <p>event altKey: {{ event.altKey }}</p>
21661
     </file>
21662
   </example>
21663
 */
21664
 
21665
 
21666
/**
21667
 * @ngdoc directive
21668
 * @name ngKeypress
21669
 *
21670
 * @description
21671
 * Specify custom behavior on keypress event.
21672
 *
21673
 * @element ANY
21674
 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
21675
 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
21676
 * and can be interrogated for keyCode, altKey, etc.)
21677
 *
21678
 * @example
21679
   <example>
21680
     <file name="index.html">
21681
      <input ng-keypress="count = count + 1" ng-init="count=0">
21682
      key press count: {{count}}
21683
     </file>
21684
   </example>
21685
 */
21686
 
21687
 
21688
/**
21689
 * @ngdoc directive
21690
 * @name ngSubmit
21691
 *
21692
 * @description
21693
 * Enables binding angular expressions to onsubmit events.
21694
 *
21695
 * Additionally it prevents the default action (which for form means sending the request to the
21696
 * server and reloading the current page), but only if the form does not contain `action`,
21697
 * `data-action`, or `x-action` attributes.
21698
 *
21699
 * <div class="alert alert-warning">
21700
 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
21701
 * `ngSubmit` handlers together. See the
21702
 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
21703
 * for a detailed discussion of when `ngSubmit` may be triggered.
21704
 * </div>
21705
 *
21706
 * @element form
21707
 * @priority 0
21708
 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
21709
 * ({@link guide/expression#-event- Event object is available as `$event`})
21710
 *
21711
 * @example
21712
   <example module="submitExample">
21713
     <file name="index.html">
21714
      <script>
21715
        angular.module('submitExample', [])
21716
          .controller('ExampleController', ['$scope', function($scope) {
21717
            $scope.list = [];
21718
            $scope.text = 'hello';
21719
            $scope.submit = function() {
21720
              if ($scope.text) {
21721
                $scope.list.push(this.text);
21722
                $scope.text = '';
21723
              }
21724
            };
21725
          }]);
21726
      </script>
21727
      <form ng-submit="submit()" ng-controller="ExampleController">
21728
        Enter text and hit enter:
21729
        <input type="text" ng-model="text" name="text" />
21730
        <input type="submit" id="submit" value="Submit" />
21731
        <pre>list={{list}}</pre>
21732
      </form>
21733
     </file>
21734
     <file name="protractor.js" type="protractor">
21735
       it('should check ng-submit', function() {
21736
         expect(element(by.binding('list')).getText()).toBe('list=[]');
21737
         element(by.css('#submit')).click();
21738
         expect(element(by.binding('list')).getText()).toContain('hello');
21739
         expect(element(by.model('text')).getAttribute('value')).toBe('');
21740
       });
21741
       it('should ignore empty strings', function() {
21742
         expect(element(by.binding('list')).getText()).toBe('list=[]');
21743
         element(by.css('#submit')).click();
21744
         element(by.css('#submit')).click();
21745
         expect(element(by.binding('list')).getText()).toContain('hello');
21746
        });
21747
     </file>
21748
   </example>
21749
 */
21750
 
21751
/**
21752
 * @ngdoc directive
21753
 * @name ngFocus
21754
 *
21755
 * @description
21756
 * Specify custom behavior on focus event.
21757
 *
21758
 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
21759
 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
21760
 * during an `$apply` to ensure a consistent state.
21761
 *
21762
 * @element window, input, select, textarea, a
21763
 * @priority 0
21764
 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
21765
 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
21766
 *
21767
 * @example
21768
 * See {@link ng.directive:ngClick ngClick}
21769
 */
21770
 
21771
/**
21772
 * @ngdoc directive
21773
 * @name ngBlur
21774
 *
21775
 * @description
21776
 * Specify custom behavior on blur event.
21777
 *
21778
 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
21779
 * an element has lost focus.
21780
 *
21781
 * Note: As the `blur` event is executed synchronously also during DOM manipulations
21782
 * (e.g. removing a focussed input),
21783
 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
21784
 * during an `$apply` to ensure a consistent state.
21785
 *
21786
 * @element window, input, select, textarea, a
21787
 * @priority 0
21788
 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
21789
 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
21790
 *
21791
 * @example
21792
 * See {@link ng.directive:ngClick ngClick}
21793
 */
21794
 
21795
/**
21796
 * @ngdoc directive
21797
 * @name ngCopy
21798
 *
21799
 * @description
21800
 * Specify custom behavior on copy event.
21801
 *
21802
 * @element window, input, select, textarea, a
21803
 * @priority 0
21804
 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
21805
 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
21806
 *
21807
 * @example
21808
   <example>
21809
     <file name="index.html">
21810
      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
21811
      copied: {{copied}}
21812
     </file>
21813
   </example>
21814
 */
21815
 
21816
/**
21817
 * @ngdoc directive
21818
 * @name ngCut
21819
 *
21820
 * @description
21821
 * Specify custom behavior on cut event.
21822
 *
21823
 * @element window, input, select, textarea, a
21824
 * @priority 0
21825
 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
21826
 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
21827
 *
21828
 * @example
21829
   <example>
21830
     <file name="index.html">
21831
      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
21832
      cut: {{cut}}
21833
     </file>
21834
   </example>
21835
 */
21836
 
21837
/**
21838
 * @ngdoc directive
21839
 * @name ngPaste
21840
 *
21841
 * @description
21842
 * Specify custom behavior on paste event.
21843
 *
21844
 * @element window, input, select, textarea, a
21845
 * @priority 0
21846
 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
21847
 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
21848
 *
21849
 * @example
21850
   <example>
21851
     <file name="index.html">
21852
      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
21853
      pasted: {{paste}}
21854
     </file>
21855
   </example>
21856
 */
21857
 
21858
/**
21859
 * @ngdoc directive
21860
 * @name ngIf
21861
 * @restrict A
21862
 *
21863
 * @description
21864
 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
21865
 * {expression}. If the expression assigned to `ngIf` evaluates to a false
21866
 * value then the element is removed from the DOM, otherwise a clone of the
21867
 * element is reinserted into the DOM.
21868
 *
21869
 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
21870
 * element in the DOM rather than changing its visibility via the `display` css property.  A common
21871
 * case when this difference is significant is when using css selectors that rely on an element's
21872
 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
21873
 *
21874
 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
21875
 * is created when the element is restored.  The scope created within `ngIf` inherits from
21876
 * its parent scope using
21877
 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
21878
 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
21879
 * a javascript primitive defined in the parent scope. In this case any modifications made to the
21880
 * variable within the child scope will override (hide) the value in the parent scope.
21881
 *
21882
 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
21883
 * is if an element's class attribute is directly modified after it's compiled, using something like
21884
 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
21885
 * the added class will be lost because the original compiled state is used to regenerate the element.
21886
 *
21887
 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
21888
 * and `leave` effects.
21889
 *
21890
 * @animations
21891
 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
21892
 * leave - happens just before the `ngIf` contents are removed from the DOM
21893
 *
21894
 * @element ANY
21895
 * @scope
21896
 * @priority 600
21897
 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
21898
 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
21899
 *     element is added to the DOM tree.
21900
 *
21901
 * @example
21902
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
21903
    <file name="index.html">
21904
      Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
21905
      Show when checked:
21906
      <span ng-if="checked" class="animate-if">
21907
        This is removed when the checkbox is unchecked.
21908
      </span>
21909
    </file>
21910
    <file name="animations.css">
21911
      .animate-if {
21912
        background:white;
21913
        border:1px solid black;
21914
        padding:10px;
21915
      }
21916
 
21917
      .animate-if.ng-enter, .animate-if.ng-leave {
21918
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21919
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21920
      }
21921
 
21922
      .animate-if.ng-enter,
21923
      .animate-if.ng-leave.ng-leave-active {
21924
        opacity:0;
21925
      }
21926
 
21927
      .animate-if.ng-leave,
21928
      .animate-if.ng-enter.ng-enter-active {
21929
        opacity:1;
21930
      }
21931
    </file>
21932
  </example>
21933
 */
21934
var ngIfDirective = ['$animate', function($animate) {
21935
  return {
21936
    multiElement: true,
21937
    transclude: 'element',
21938
    priority: 600,
21939
    terminal: true,
21940
    restrict: 'A',
21941
    $$tlb: true,
21942
    link: function($scope, $element, $attr, ctrl, $transclude) {
21943
        var block, childScope, previousElements;
21944
        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
21945
 
21946
          if (value) {
21947
            if (!childScope) {
21948
              $transclude(function(clone, newScope) {
21949
                childScope = newScope;
21950
                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
21951
                // Note: We only need the first/last node of the cloned nodes.
21952
                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
21953
                // by a directive with templateUrl when its template arrives.
21954
                block = {
21955
                  clone: clone
21956
                };
21957
                $animate.enter(clone, $element.parent(), $element);
21958
              });
21959
            }
21960
          } else {
21961
            if (previousElements) {
21962
              previousElements.remove();
21963
              previousElements = null;
21964
            }
21965
            if (childScope) {
21966
              childScope.$destroy();
21967
              childScope = null;
21968
            }
21969
            if (block) {
21970
              previousElements = getBlockNodes(block.clone);
21971
              $animate.leave(previousElements).then(function() {
21972
                previousElements = null;
21973
              });
21974
              block = null;
21975
            }
21976
          }
21977
        });
21978
    }
21979
  };
21980
}];
21981
 
21982
/**
21983
 * @ngdoc directive
21984
 * @name ngInclude
21985
 * @restrict ECA
21986
 *
21987
 * @description
21988
 * Fetches, compiles and includes an external HTML fragment.
21989
 *
21990
 * By default, the template URL is restricted to the same domain and protocol as the
21991
 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
21992
 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
21993
 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
21994
 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
21995
 * ng.$sce Strict Contextual Escaping}.
21996
 *
21997
 * In addition, the browser's
21998
 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
21999
 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
22000
 * policy may further restrict whether the template is successfully loaded.
22001
 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
22002
 * access on some browsers.
22003
 *
22004
 * @animations
22005
 * enter - animation is used to bring new content into the browser.
22006
 * leave - animation is used to animate existing content away.
22007
 *
22008
 * The enter and leave animation occur concurrently.
22009
 *
22010
 * @scope
22011
 * @priority 400
22012
 *
22013
 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
22014
 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
22015
 * @param {string=} onload Expression to evaluate when a new partial is loaded.
22016
 *
22017
 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
22018
 *                  $anchorScroll} to scroll the viewport after the content is loaded.
22019
 *
22020
 *                  - If the attribute is not set, disable scrolling.
22021
 *                  - If the attribute is set without value, enable scrolling.
22022
 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
22023
 *
22024
 * @example
22025
  <example module="includeExample" deps="angular-animate.js" animations="true">
22026
    <file name="index.html">
22027
     <div ng-controller="ExampleController">
22028
       <select ng-model="template" ng-options="t.name for t in templates">
22029
        <option value="">(blank)</option>
22030
       </select>
22031
       url of the template: <code>{{template.url}}</code>
22032
       <hr/>
22033
       <div class="slide-animate-container">
22034
         <div class="slide-animate" ng-include="template.url"></div>
22035
       </div>
22036
     </div>
22037
    </file>
22038
    <file name="script.js">
22039
      angular.module('includeExample', ['ngAnimate'])
22040
        .controller('ExampleController', ['$scope', function($scope) {
22041
          $scope.templates =
22042
            [ { name: 'template1.html', url: 'template1.html'},
22043
              { name: 'template2.html', url: 'template2.html'} ];
22044
          $scope.template = $scope.templates[0];
22045
        }]);
22046
     </file>
22047
    <file name="template1.html">
22048
      Content of template1.html
22049
    </file>
22050
    <file name="template2.html">
22051
      Content of template2.html
22052
    </file>
22053
    <file name="animations.css">
22054
      .slide-animate-container {
22055
        position:relative;
22056
        background:white;
22057
        border:1px solid black;
22058
        height:40px;
22059
        overflow:hidden;
22060
      }
22061
 
22062
      .slide-animate {
22063
        padding:10px;
22064
      }
22065
 
22066
      .slide-animate.ng-enter, .slide-animate.ng-leave {
22067
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22068
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22069
 
22070
        position:absolute;
22071
        top:0;
22072
        left:0;
22073
        right:0;
22074
        bottom:0;
22075
        display:block;
22076
        padding:10px;
22077
      }
22078
 
22079
      .slide-animate.ng-enter {
22080
        top:-50px;
22081
      }
22082
      .slide-animate.ng-enter.ng-enter-active {
22083
        top:0;
22084
      }
22085
 
22086
      .slide-animate.ng-leave {
22087
        top:0;
22088
      }
22089
      .slide-animate.ng-leave.ng-leave-active {
22090
        top:50px;
22091
      }
22092
    </file>
22093
    <file name="protractor.js" type="protractor">
22094
      var templateSelect = element(by.model('template'));
22095
      var includeElem = element(by.css('[ng-include]'));
22096
 
22097
      it('should load template1.html', function() {
22098
        expect(includeElem.getText()).toMatch(/Content of template1.html/);
22099
      });
22100
 
22101
      it('should load template2.html', function() {
22102
        if (browser.params.browser == 'firefox') {
22103
          // Firefox can't handle using selects
22104
          // See https://github.com/angular/protractor/issues/480
22105
          return;
22106
        }
22107
        templateSelect.click();
22108
        templateSelect.all(by.css('option')).get(2).click();
22109
        expect(includeElem.getText()).toMatch(/Content of template2.html/);
22110
      });
22111
 
22112
      it('should change to blank', function() {
22113
        if (browser.params.browser == 'firefox') {
22114
          // Firefox can't handle using selects
22115
          return;
22116
        }
22117
        templateSelect.click();
22118
        templateSelect.all(by.css('option')).get(0).click();
22119
        expect(includeElem.isPresent()).toBe(false);
22120
      });
22121
    </file>
22122
  </example>
22123
 */
22124
 
22125
 
22126
/**
22127
 * @ngdoc event
22128
 * @name ngInclude#$includeContentRequested
22129
 * @eventType emit on the scope ngInclude was declared in
22130
 * @description
22131
 * Emitted every time the ngInclude content is requested.
22132
 *
22133
 * @param {Object} angularEvent Synthetic event object.
22134
 * @param {String} src URL of content to load.
22135
 */
22136
 
22137
 
22138
/**
22139
 * @ngdoc event
22140
 * @name ngInclude#$includeContentLoaded
22141
 * @eventType emit on the current ngInclude scope
22142
 * @description
22143
 * Emitted every time the ngInclude content is reloaded.
22144
 *
22145
 * @param {Object} angularEvent Synthetic event object.
22146
 * @param {String} src URL of content to load.
22147
 */
22148
 
22149
 
22150
/**
22151
 * @ngdoc event
22152
 * @name ngInclude#$includeContentError
22153
 * @eventType emit on the scope ngInclude was declared in
22154
 * @description
22155
 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
22156
 *
22157
 * @param {Object} angularEvent Synthetic event object.
22158
 * @param {String} src URL of content to load.
22159
 */
22160
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
22161
                  function($templateRequest,   $anchorScroll,   $animate,   $sce) {
22162
  return {
22163
    restrict: 'ECA',
22164
    priority: 400,
22165
    terminal: true,
22166
    transclude: 'element',
22167
    controller: angular.noop,
22168
    compile: function(element, attr) {
22169
      var srcExp = attr.ngInclude || attr.src,
22170
          onloadExp = attr.onload || '',
22171
          autoScrollExp = attr.autoscroll;
22172
 
22173
      return function(scope, $element, $attr, ctrl, $transclude) {
22174
        var changeCounter = 0,
22175
            currentScope,
22176
            previousElement,
22177
            currentElement;
22178
 
22179
        var cleanupLastIncludeContent = function() {
22180
          if (previousElement) {
22181
            previousElement.remove();
22182
            previousElement = null;
22183
          }
22184
          if (currentScope) {
22185
            currentScope.$destroy();
22186
            currentScope = null;
22187
          }
22188
          if (currentElement) {
22189
            $animate.leave(currentElement).then(function() {
22190
              previousElement = null;
22191
            });
22192
            previousElement = currentElement;
22193
            currentElement = null;
22194
          }
22195
        };
22196
 
22197
        scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
22198
          var afterAnimation = function() {
22199
            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
22200
              $anchorScroll();
22201
            }
22202
          };
22203
          var thisChangeId = ++changeCounter;
22204
 
22205
          if (src) {
22206
            //set the 2nd param to true to ignore the template request error so that the inner
22207
            //contents and scope can be cleaned up.
22208
            $templateRequest(src, true).then(function(response) {
22209
              if (thisChangeId !== changeCounter) return;
22210
              var newScope = scope.$new();
22211
              ctrl.template = response;
22212
 
22213
              // Note: This will also link all children of ng-include that were contained in the original
22214
              // html. If that content contains controllers, ... they could pollute/change the scope.
22215
              // However, using ng-include on an element with additional content does not make sense...
22216
              // Note: We can't remove them in the cloneAttchFn of $transclude as that
22217
              // function is called before linking the content, which would apply child
22218
              // directives to non existing elements.
22219
              var clone = $transclude(newScope, function(clone) {
22220
                cleanupLastIncludeContent();
22221
                $animate.enter(clone, null, $element).then(afterAnimation);
22222
              });
22223
 
22224
              currentScope = newScope;
22225
              currentElement = clone;
22226
 
22227
              currentScope.$emit('$includeContentLoaded', src);
22228
              scope.$eval(onloadExp);
22229
            }, function() {
22230
              if (thisChangeId === changeCounter) {
22231
                cleanupLastIncludeContent();
22232
                scope.$emit('$includeContentError', src);
22233
              }
22234
            });
22235
            scope.$emit('$includeContentRequested', src);
22236
          } else {
22237
            cleanupLastIncludeContent();
22238
            ctrl.template = null;
22239
          }
22240
        });
22241
      };
22242
    }
22243
  };
22244
}];
22245
 
22246
// This directive is called during the $transclude call of the first `ngInclude` directive.
22247
// It will replace and compile the content of the element with the loaded template.
22248
// We need this directive so that the element content is already filled when
22249
// the link function of another directive on the same element as ngInclude
22250
// is called.
22251
var ngIncludeFillContentDirective = ['$compile',
22252
  function($compile) {
22253
    return {
22254
      restrict: 'ECA',
22255
      priority: -400,
22256
      require: 'ngInclude',
22257
      link: function(scope, $element, $attr, ctrl) {
22258
        if (/SVG/.test($element[0].toString())) {
22259
          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
22260
          // support innerHTML, so detect this here and try to generate the contents
22261
          // specially.
22262
          $element.empty();
22263
          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
22264
              function namespaceAdaptedClone(clone) {
22265
            $element.append(clone);
22266
          }, {futureParentElement: $element});
22267
          return;
22268
        }
22269
 
22270
        $element.html(ctrl.template);
22271
        $compile($element.contents())(scope);
22272
      }
22273
    };
22274
  }];
22275
 
22276
/**
22277
 * @ngdoc directive
22278
 * @name ngInit
22279
 * @restrict AC
22280
 *
22281
 * @description
22282
 * The `ngInit` directive allows you to evaluate an expression in the
22283
 * current scope.
22284
 *
22285
 * <div class="alert alert-error">
22286
 * The only appropriate use of `ngInit` is for aliasing special properties of
22287
 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
22288
 * should use {@link guide/controller controllers} rather than `ngInit`
22289
 * to initialize values on a scope.
22290
 * </div>
22291
 * <div class="alert alert-warning">
22292
 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
22293
 * sure you have parenthesis for correct precedence:
22294
 * <pre class="prettyprint">
22295
 * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
22296
 * </pre>
22297
 * </div>
22298
 *
22299
 * @priority 450
22300
 *
22301
 * @element ANY
22302
 * @param {expression} ngInit {@link guide/expression Expression} to eval.
22303
 *
22304
 * @example
22305
   <example module="initExample">
22306
     <file name="index.html">
22307
   <script>
22308
     angular.module('initExample', [])
22309
       .controller('ExampleController', ['$scope', function($scope) {
22310
         $scope.list = [['a', 'b'], ['c', 'd']];
22311
       }]);
22312
   </script>
22313
   <div ng-controller="ExampleController">
22314
     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
22315
       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
22316
          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
22317
       </div>
22318
     </div>
22319
   </div>
22320
     </file>
22321
     <file name="protractor.js" type="protractor">
22322
       it('should alias index positions', function() {
22323
         var elements = element.all(by.css('.example-init'));
22324
         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
22325
         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
22326
         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
22327
         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
22328
       });
22329
     </file>
22330
   </example>
22331
 */
22332
var ngInitDirective = ngDirective({
22333
  priority: 450,
22334
  compile: function() {
22335
    return {
22336
      pre: function(scope, element, attrs) {
22337
        scope.$eval(attrs.ngInit);
22338
      }
22339
    };
22340
  }
22341
});
22342
 
22343
/**
22344
 * @ngdoc directive
22345
 * @name ngList
22346
 *
22347
 * @description
22348
 * Text input that converts between a delimited string and an array of strings. The default
22349
 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
22350
 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
22351
 *
22352
 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
22353
 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
22354
 *   list item is respected. This implies that the user of the directive is responsible for
22355
 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
22356
 *   tab or newline character.
22357
 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
22358
 *   when joining the list items back together) and whitespace around each list item is stripped
22359
 *   before it is added to the model.
22360
 *
22361
 * ### Example with Validation
22362
 *
22363
 * <example name="ngList-directive" module="listExample">
22364
 *   <file name="app.js">
22365
 *      angular.module('listExample', [])
22366
 *        .controller('ExampleController', ['$scope', function($scope) {
22367
 *          $scope.names = ['morpheus', 'neo', 'trinity'];
22368
 *        }]);
22369
 *   </file>
22370
 *   <file name="index.html">
22371
 *    <form name="myForm" ng-controller="ExampleController">
22372
 *      List: <input name="namesInput" ng-model="names" ng-list required>
22373
 *      <span class="error" ng-show="myForm.namesInput.$error.required">
22374
 *        Required!</span>
22375
 *      <br>
22376
 *      <tt>names = {{names}}</tt><br/>
22377
 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
22378
 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
22379
 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22380
 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22381
 *     </form>
22382
 *   </file>
22383
 *   <file name="protractor.js" type="protractor">
22384
 *     var listInput = element(by.model('names'));
22385
 *     var names = element(by.exactBinding('names'));
22386
 *     var valid = element(by.binding('myForm.namesInput.$valid'));
22387
 *     var error = element(by.css('span.error'));
22388
 *
22389
 *     it('should initialize to model', function() {
22390
 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
22391
 *       expect(valid.getText()).toContain('true');
22392
 *       expect(error.getCssValue('display')).toBe('none');
22393
 *     });
22394
 *
22395
 *     it('should be invalid if empty', function() {
22396
 *       listInput.clear();
22397
 *       listInput.sendKeys('');
22398
 *
22399
 *       expect(names.getText()).toContain('');
22400
 *       expect(valid.getText()).toContain('false');
22401
 *       expect(error.getCssValue('display')).not.toBe('none');
22402
 *     });
22403
 *   </file>
22404
 * </example>
22405
 *
22406
 * ### Example - splitting on whitespace
22407
 * <example name="ngList-directive-newlines">
22408
 *   <file name="index.html">
22409
 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
22410
 *    <pre>{{ list | json }}</pre>
22411
 *   </file>
22412
 *   <file name="protractor.js" type="protractor">
22413
 *     it("should split the text by newlines", function() {
22414
 *       var listInput = element(by.model('list'));
22415
 *       var output = element(by.binding('list | json'));
22416
 *       listInput.sendKeys('abc\ndef\nghi');
22417
 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
22418
 *     });
22419
 *   </file>
22420
 * </example>
22421
 *
22422
 * @element input
22423
 * @param {string=} ngList optional delimiter that should be used to split the value.
22424
 */
22425
var ngListDirective = function() {
22426
  return {
22427
    restrict: 'A',
22428
    priority: 100,
22429
    require: 'ngModel',
22430
    link: function(scope, element, attr, ctrl) {
22431
      // We want to control whitespace trimming so we use this convoluted approach
22432
      // to access the ngList attribute, which doesn't pre-trim the attribute
22433
      var ngList = element.attr(attr.$attr.ngList) || ', ';
22434
      var trimValues = attr.ngTrim !== 'false';
22435
      var separator = trimValues ? trim(ngList) : ngList;
22436
 
22437
      var parse = function(viewValue) {
22438
        // If the viewValue is invalid (say required but empty) it will be `undefined`
22439
        if (isUndefined(viewValue)) return;
22440
 
22441
        var list = [];
22442
 
22443
        if (viewValue) {
22444
          forEach(viewValue.split(separator), function(value) {
22445
            if (value) list.push(trimValues ? trim(value) : value);
22446
          });
22447
        }
22448
 
22449
        return list;
22450
      };
22451
 
22452
      ctrl.$parsers.push(parse);
22453
      ctrl.$formatters.push(function(value) {
22454
        if (isArray(value)) {
22455
          return value.join(ngList);
22456
        }
22457
 
22458
        return undefined;
22459
      });
22460
 
22461
      // Override the standard $isEmpty because an empty array means the input is empty.
22462
      ctrl.$isEmpty = function(value) {
22463
        return !value || !value.length;
22464
      };
22465
    }
22466
  };
22467
};
22468
 
22469
/* global VALID_CLASS: true,
22470
  INVALID_CLASS: true,
22471
  PRISTINE_CLASS: true,
22472
  DIRTY_CLASS: true,
22473
  UNTOUCHED_CLASS: true,
22474
  TOUCHED_CLASS: true,
22475
*/
22476
 
22477
var VALID_CLASS = 'ng-valid',
22478
    INVALID_CLASS = 'ng-invalid',
22479
    PRISTINE_CLASS = 'ng-pristine',
22480
    DIRTY_CLASS = 'ng-dirty',
22481
    UNTOUCHED_CLASS = 'ng-untouched',
22482
    TOUCHED_CLASS = 'ng-touched',
22483
    PENDING_CLASS = 'ng-pending';
22484
 
22485
 
22486
var $ngModelMinErr = new minErr('ngModel');
22487
 
22488
/**
22489
 * @ngdoc type
22490
 * @name ngModel.NgModelController
22491
 *
22492
 * @property {string} $viewValue Actual string value in the view.
22493
 * @property {*} $modelValue The value in the model that the control is bound to.
22494
 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
22495
       the control reads value from the DOM. The functions are called in array order, each passing
22496
       its return value through to the next. The last return value is forwarded to the
22497
       {@link ngModel.NgModelController#$validators `$validators`} collection.
22498
 
22499
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
22500
`$viewValue`}.
22501
 
22502
Returning `undefined` from a parser means a parse error occurred. In that case,
22503
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
22504
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
22505
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
22506
 
22507
 *
22508
 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
22509
       the model value changes. The functions are called in reverse array order, each passing the value through to the
22510
       next. The last return value is used as the actual DOM value.
22511
       Used to format / convert values for display in the control.
22512
 * ```js
22513
 * function formatter(value) {
22514
 *   if (value) {
22515
 *     return value.toUpperCase();
22516
 *   }
22517
 * }
22518
 * ngModel.$formatters.push(formatter);
22519
 * ```
22520
 *
22521
 * @property {Object.<string, function>} $validators A collection of validators that are applied
22522
 *      whenever the model value changes. The key value within the object refers to the name of the
22523
 *      validator while the function refers to the validation operation. The validation operation is
22524
 *      provided with the model value as an argument and must return a true or false value depending
22525
 *      on the response of that validation.
22526
 *
22527
 * ```js
22528
 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
22529
 *   var value = modelValue || viewValue;
22530
 *   return /[0-9]+/.test(value) &&
22531
 *          /[a-z]+/.test(value) &&
22532
 *          /[A-Z]+/.test(value) &&
22533
 *          /\W+/.test(value);
22534
 * };
22535
 * ```
22536
 *
22537
 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
22538
 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
22539
 *      is expected to return a promise when it is run during the model validation process. Once the promise
22540
 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
22541
 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
22542
 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
22543
 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
22544
 *      will only run once all synchronous validators have passed.
22545
 *
22546
 * Please note that if $http is used then it is important that the server returns a success HTTP response code
22547
 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
22548
 *
22549
 * ```js
22550
 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
22551
 *   var value = modelValue || viewValue;
22552
 *
22553
 *   // Lookup user by username
22554
 *   return $http.get('/api/users/' + value).
22555
 *      then(function resolved() {
22556
 *        //username exists, this means validation fails
22557
 *        return $q.reject('exists');
22558
 *      }, function rejected() {
22559
 *        //username does not exist, therefore this validation passes
22560
 *        return true;
22561
 *      });
22562
 * };
22563
 * ```
22564
 *
22565
 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
22566
 *     view value has changed. It is called with no arguments, and its return value is ignored.
22567
 *     This can be used in place of additional $watches against the model value.
22568
 *
22569
 * @property {Object} $error An object hash with all failing validator ids as keys.
22570
 * @property {Object} $pending An object hash with all pending validator ids as keys.
22571
 *
22572
 * @property {boolean} $untouched True if control has not lost focus yet.
22573
 * @property {boolean} $touched True if control has lost focus.
22574
 * @property {boolean} $pristine True if user has not interacted with the control yet.
22575
 * @property {boolean} $dirty True if user has already interacted with the control.
22576
 * @property {boolean} $valid True if there is no error.
22577
 * @property {boolean} $invalid True if at least one error on the control.
22578
 * @property {string} $name The name attribute of the control.
22579
 *
22580
 * @description
22581
 *
22582
 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
22583
 * The controller contains services for data-binding, validation, CSS updates, and value formatting
22584
 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
22585
 * listening to DOM events.
22586
 * Such DOM related logic should be provided by other directives which make use of
22587
 * `NgModelController` for data-binding to control elements.
22588
 * Angular provides this DOM logic for most {@link input `input`} elements.
22589
 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
22590
 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
22591
 *
22592
 * @example
22593
 * ### Custom Control Example
22594
 * This example shows how to use `NgModelController` with a custom control to achieve
22595
 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
22596
 * collaborate together to achieve the desired result.
22597
 *
22598
 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
22599
 * contents be edited in place by the user.  This will not work on older browsers.
22600
 *
22601
 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
22602
 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
22603
 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
22604
 * that content using the `$sce` service.
22605
 *
22606
 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
22607
    <file name="style.css">
22608
      [contenteditable] {
22609
        border: 1px solid black;
22610
        background-color: white;
22611
        min-height: 20px;
22612
      }
22613
 
22614
      .ng-invalid {
22615
        border: 1px solid red;
22616
      }
22617
 
22618
    </file>
22619
    <file name="script.js">
22620
      angular.module('customControl', ['ngSanitize']).
22621
        directive('contenteditable', ['$sce', function($sce) {
22622
          return {
22623
            restrict: 'A', // only activate on element attribute
22624
            require: '?ngModel', // get a hold of NgModelController
22625
            link: function(scope, element, attrs, ngModel) {
22626
              if (!ngModel) return; // do nothing if no ng-model
22627
 
22628
              // Specify how UI should be updated
22629
              ngModel.$render = function() {
22630
                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
22631
              };
22632
 
22633
              // Listen for change events to enable binding
22634
              element.on('blur keyup change', function() {
22635
                scope.$evalAsync(read);
22636
              });
22637
              read(); // initialize
22638
 
22639
              // Write data to the model
22640
              function read() {
22641
                var html = element.html();
22642
                // When we clear the content editable the browser leaves a <br> behind
22643
                // If strip-br attribute is provided then we strip this out
22644
                if ( attrs.stripBr && html == '<br>' ) {
22645
                  html = '';
22646
                }
22647
                ngModel.$setViewValue(html);
22648
              }
22649
            }
22650
          };
22651
        }]);
22652
    </file>
22653
    <file name="index.html">
22654
      <form name="myForm">
22655
       <div contenteditable
22656
            name="myWidget" ng-model="userContent"
22657
            strip-br="true"
22658
            required>Change me!</div>
22659
        <span ng-show="myForm.myWidget.$error.required">Required!</span>
22660
       <hr>
22661
       <textarea ng-model="userContent"></textarea>
22662
      </form>
22663
    </file>
22664
    <file name="protractor.js" type="protractor">
22665
    it('should data-bind and become invalid', function() {
22666
      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
22667
        // SafariDriver can't handle contenteditable
22668
        // and Firefox driver can't clear contenteditables very well
22669
        return;
22670
      }
22671
      var contentEditable = element(by.css('[contenteditable]'));
22672
      var content = 'Change me!';
22673
 
22674
      expect(contentEditable.getText()).toEqual(content);
22675
 
22676
      contentEditable.clear();
22677
      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
22678
      expect(contentEditable.getText()).toEqual('');
22679
      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
22680
    });
22681
    </file>
22682
 * </example>
22683
 *
22684
 *
22685
 */
22686
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
22687
    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
22688
  this.$viewValue = Number.NaN;
22689
  this.$modelValue = Number.NaN;
22690
  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
22691
  this.$validators = {};
22692
  this.$asyncValidators = {};
22693
  this.$parsers = [];
22694
  this.$formatters = [];
22695
  this.$viewChangeListeners = [];
22696
  this.$untouched = true;
22697
  this.$touched = false;
22698
  this.$pristine = true;
22699
  this.$dirty = false;
22700
  this.$valid = true;
22701
  this.$invalid = false;
22702
  this.$error = {}; // keep invalid keys here
22703
  this.$$success = {}; // keep valid keys here
22704
  this.$pending = undefined; // keep pending keys here
22705
  this.$name = $interpolate($attr.name || '', false)($scope);
22706
 
22707
 
22708
  var parsedNgModel = $parse($attr.ngModel),
22709
      parsedNgModelAssign = parsedNgModel.assign,
22710
      ngModelGet = parsedNgModel,
22711
      ngModelSet = parsedNgModelAssign,
22712
      pendingDebounce = null,
22713
      ctrl = this;
22714
 
22715
  this.$$setOptions = function(options) {
22716
    ctrl.$options = options;
22717
    if (options && options.getterSetter) {
22718
      var invokeModelGetter = $parse($attr.ngModel + '()'),
22719
          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
22720
 
22721
      ngModelGet = function($scope) {
22722
        var modelValue = parsedNgModel($scope);
22723
        if (isFunction(modelValue)) {
22724
          modelValue = invokeModelGetter($scope);
22725
        }
22726
        return modelValue;
22727
      };
22728
      ngModelSet = function($scope, newValue) {
22729
        if (isFunction(parsedNgModel($scope))) {
22730
          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
22731
        } else {
22732
          parsedNgModelAssign($scope, ctrl.$modelValue);
22733
        }
22734
      };
22735
    } else if (!parsedNgModel.assign) {
22736
      throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
22737
          $attr.ngModel, startingTag($element));
22738
    }
22739
  };
22740
 
22741
  /**
22742
   * @ngdoc method
22743
   * @name ngModel.NgModelController#$render
22744
   *
22745
   * @description
22746
   * Called when the view needs to be updated. It is expected that the user of the ng-model
22747
   * directive will implement this method.
22748
   *
22749
   * The `$render()` method is invoked in the following situations:
22750
   *
22751
   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
22752
   *   committed value then `$render()` is called to update the input control.
22753
   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
22754
   *   the `$viewValue` are different to last time.
22755
   *
22756
   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
22757
   * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`
22758
   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
22759
   * invoked if you only change a property on the objects.
22760
   */
22761
  this.$render = noop;
22762
 
22763
  /**
22764
   * @ngdoc method
22765
   * @name ngModel.NgModelController#$isEmpty
22766
   *
22767
   * @description
22768
   * This is called when we need to determine if the value of an input is empty.
22769
   *
22770
   * For instance, the required directive does this to work out if the input has data or not.
22771
   *
22772
   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
22773
   *
22774
   * You can override this for input directives whose concept of being empty is different to the
22775
   * default. The `checkboxInputType` directive does this because in its case a value of `false`
22776
   * implies empty.
22777
   *
22778
   * @param {*} value The value of the input to check for emptiness.
22779
   * @returns {boolean} True if `value` is "empty".
22780
   */
22781
  this.$isEmpty = function(value) {
22782
    return isUndefined(value) || value === '' || value === null || value !== value;
22783
  };
22784
 
22785
  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
22786
      currentValidationRunId = 0;
22787
 
22788
  /**
22789
   * @ngdoc method
22790
   * @name ngModel.NgModelController#$setValidity
22791
   *
22792
   * @description
22793
   * Change the validity state, and notify the form.
22794
   *
22795
   * This method can be called within $parsers/$formatters or a custom validation implementation.
22796
   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
22797
   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
22798
   *
22799
   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
22800
   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
22801
   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
22802
   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
22803
   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
22804
   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
22805
   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
22806
   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
22807
   *                          Skipped is used by Angular when validators do not run because of parse errors and
22808
   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
22809
   */
22810
  addSetValidityMethod({
22811
    ctrl: this,
22812
    $element: $element,
22813
    set: function(object, property) {
22814
      object[property] = true;
22815
    },
22816
    unset: function(object, property) {
22817
      delete object[property];
22818
    },
22819
    parentForm: parentForm,
22820
    $animate: $animate
22821
  });
22822
 
22823
  /**
22824
   * @ngdoc method
22825
   * @name ngModel.NgModelController#$setPristine
22826
   *
22827
   * @description
22828
   * Sets the control to its pristine state.
22829
   *
22830
   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
22831
   * state (`ng-pristine` class). A model is considered to be pristine when the control
22832
   * has not been changed from when first compiled.
22833
   */
22834
  this.$setPristine = function() {
22835
    ctrl.$dirty = false;
22836
    ctrl.$pristine = true;
22837
    $animate.removeClass($element, DIRTY_CLASS);
22838
    $animate.addClass($element, PRISTINE_CLASS);
22839
  };
22840
 
22841
  /**
22842
   * @ngdoc method
22843
   * @name ngModel.NgModelController#$setDirty
22844
   *
22845
   * @description
22846
   * Sets the control to its dirty state.
22847
   *
22848
   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
22849
   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
22850
   * from when first compiled.
22851
   */
22852
  this.$setDirty = function() {
22853
    ctrl.$dirty = true;
22854
    ctrl.$pristine = false;
22855
    $animate.removeClass($element, PRISTINE_CLASS);
22856
    $animate.addClass($element, DIRTY_CLASS);
22857
    parentForm.$setDirty();
22858
  };
22859
 
22860
  /**
22861
   * @ngdoc method
22862
   * @name ngModel.NgModelController#$setUntouched
22863
   *
22864
   * @description
22865
   * Sets the control to its untouched state.
22866
   *
22867
   * This method can be called to remove the `ng-touched` class and set the control to its
22868
   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
22869
   * by default, however this function can be used to restore that state if the model has
22870
   * already been touched by the user.
22871
   */
22872
  this.$setUntouched = function() {
22873
    ctrl.$touched = false;
22874
    ctrl.$untouched = true;
22875
    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
22876
  };
22877
 
22878
  /**
22879
   * @ngdoc method
22880
   * @name ngModel.NgModelController#$setTouched
22881
   *
22882
   * @description
22883
   * Sets the control to its touched state.
22884
   *
22885
   * This method can be called to remove the `ng-untouched` class and set the control to its
22886
   * touched state (`ng-touched` class). A model is considered to be touched when the user has
22887
   * first focused the control element and then shifted focus away from the control (blur event).
22888
   */
22889
  this.$setTouched = function() {
22890
    ctrl.$touched = true;
22891
    ctrl.$untouched = false;
22892
    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
22893
  };
22894
 
22895
  /**
22896
   * @ngdoc method
22897
   * @name ngModel.NgModelController#$rollbackViewValue
22898
   *
22899
   * @description
22900
   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
22901
   * which may be caused by a pending debounced event or because the input is waiting for a some
22902
   * future event.
22903
   *
22904
   * If you have an input that uses `ng-model-options` to set up debounced events or events such
22905
   * as blur you can have a situation where there is a period when the `$viewValue`
22906
   * is out of synch with the ngModel's `$modelValue`.
22907
   *
22908
   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
22909
   * programmatically before these debounced/future events have resolved/occurred, because Angular's
22910
   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
22911
   *
22912
   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
22913
   * input which may have such events pending. This is important in order to make sure that the
22914
   * input field will be updated with the new model value and any pending operations are cancelled.
22915
   *
22916
   * <example name="ng-model-cancel-update" module="cancel-update-example">
22917
   *   <file name="app.js">
22918
   *     angular.module('cancel-update-example', [])
22919
   *
22920
   *     .controller('CancelUpdateController', ['$scope', function($scope) {
22921
   *       $scope.resetWithCancel = function(e) {
22922
   *         if (e.keyCode == 27) {
22923
   *           $scope.myForm.myInput1.$rollbackViewValue();
22924
   *           $scope.myValue = '';
22925
   *         }
22926
   *       };
22927
   *       $scope.resetWithoutCancel = function(e) {
22928
   *         if (e.keyCode == 27) {
22929
   *           $scope.myValue = '';
22930
   *         }
22931
   *       };
22932
   *     }]);
22933
   *   </file>
22934
   *   <file name="index.html">
22935
   *     <div ng-controller="CancelUpdateController">
22936
   *       <p>Try typing something in each input.  See that the model only updates when you
22937
   *          blur off the input.
22938
   *        </p>
22939
   *        <p>Now see what happens if you start typing then press the Escape key</p>
22940
   *
22941
   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
22942
   *         <p>With $rollbackViewValue()</p>
22943
   *         <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/>
22944
   *         myValue: "{{ myValue }}"
22945
   *
22946
   *         <p>Without $rollbackViewValue()</p>
22947
   *         <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/>
22948
   *         myValue: "{{ myValue }}"
22949
   *       </form>
22950
   *     </div>
22951
   *   </file>
22952
   * </example>
22953
   */
22954
  this.$rollbackViewValue = function() {
22955
    $timeout.cancel(pendingDebounce);
22956
    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
22957
    ctrl.$render();
22958
  };
22959
 
22960
  /**
22961
   * @ngdoc method
22962
   * @name ngModel.NgModelController#$validate
22963
   *
22964
   * @description
22965
   * Runs each of the registered validators (first synchronous validators and then
22966
   * asynchronous validators).
22967
   * If the validity changes to invalid, the model will be set to `undefined`,
22968
   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
22969
   * If the validity changes to valid, it will set the model to the last available valid
22970
   * modelValue, i.e. either the last parsed value or the last value set from the scope.
22971
   */
22972
  this.$validate = function() {
22973
    // ignore $validate before model is initialized
22974
    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
22975
      return;
22976
    }
22977
 
22978
    var viewValue = ctrl.$$lastCommittedViewValue;
22979
    // Note: we use the $$rawModelValue as $modelValue might have been
22980
    // set to undefined during a view -> model update that found validation
22981
    // errors. We can't parse the view here, since that could change
22982
    // the model although neither viewValue nor the model on the scope changed
22983
    var modelValue = ctrl.$$rawModelValue;
22984
 
22985
    // Check if the there's a parse error, so we don't unset it accidentially
22986
    var parserName = ctrl.$$parserName || 'parse';
22987
    var parserValid = ctrl.$error[parserName] ? false : undefined;
22988
 
22989
    var prevValid = ctrl.$valid;
22990
    var prevModelValue = ctrl.$modelValue;
22991
 
22992
    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
22993
 
22994
    ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
22995
      // If there was no change in validity, don't update the model
22996
      // This prevents changing an invalid modelValue to undefined
22997
      if (!allowInvalid && prevValid !== allValid) {
22998
        // Note: Don't check ctrl.$valid here, as we could have
22999
        // external validators (e.g. calculated on the server),
23000
        // that just call $setValidity and need the model value
23001
        // to calculate their validity.
23002
        ctrl.$modelValue = allValid ? modelValue : undefined;
23003
 
23004
        if (ctrl.$modelValue !== prevModelValue) {
23005
          ctrl.$$writeModelToScope();
23006
        }
23007
      }
23008
    });
23009
 
23010
  };
23011
 
23012
  this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) {
23013
    currentValidationRunId++;
23014
    var localValidationRunId = currentValidationRunId;
23015
 
23016
    // check parser error
23017
    if (!processParseErrors(parseValid)) {
23018
      validationDone(false);
23019
      return;
23020
    }
23021
    if (!processSyncValidators()) {
23022
      validationDone(false);
23023
      return;
23024
    }
23025
    processAsyncValidators();
23026
 
23027
    function processParseErrors(parseValid) {
23028
      var errorKey = ctrl.$$parserName || 'parse';
23029
      if (parseValid === undefined) {
23030
        setValidity(errorKey, null);
23031
      } else {
23032
        setValidity(errorKey, parseValid);
23033
        if (!parseValid) {
23034
          forEach(ctrl.$validators, function(v, name) {
23035
            setValidity(name, null);
23036
          });
23037
          forEach(ctrl.$asyncValidators, function(v, name) {
23038
            setValidity(name, null);
23039
          });
23040
          return false;
23041
        }
23042
      }
23043
      return true;
23044
    }
23045
 
23046
    function processSyncValidators() {
23047
      var syncValidatorsValid = true;
23048
      forEach(ctrl.$validators, function(validator, name) {
23049
        var result = validator(modelValue, viewValue);
23050
        syncValidatorsValid = syncValidatorsValid && result;
23051
        setValidity(name, result);
23052
      });
23053
      if (!syncValidatorsValid) {
23054
        forEach(ctrl.$asyncValidators, function(v, name) {
23055
          setValidity(name, null);
23056
        });
23057
        return false;
23058
      }
23059
      return true;
23060
    }
23061
 
23062
    function processAsyncValidators() {
23063
      var validatorPromises = [];
23064
      var allValid = true;
23065
      forEach(ctrl.$asyncValidators, function(validator, name) {
23066
        var promise = validator(modelValue, viewValue);
23067
        if (!isPromiseLike(promise)) {
23068
          throw $ngModelMinErr("$asyncValidators",
23069
            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
23070
        }
23071
        setValidity(name, undefined);
23072
        validatorPromises.push(promise.then(function() {
23073
          setValidity(name, true);
23074
        }, function(error) {
23075
          allValid = false;
23076
          setValidity(name, false);
23077
        }));
23078
      });
23079
      if (!validatorPromises.length) {
23080
        validationDone(true);
23081
      } else {
23082
        $q.all(validatorPromises).then(function() {
23083
          validationDone(allValid);
23084
        }, noop);
23085
      }
23086
    }
23087
 
23088
    function setValidity(name, isValid) {
23089
      if (localValidationRunId === currentValidationRunId) {
23090
        ctrl.$setValidity(name, isValid);
23091
      }
23092
    }
23093
 
23094
    function validationDone(allValid) {
23095
      if (localValidationRunId === currentValidationRunId) {
23096
 
23097
        doneCallback(allValid);
23098
      }
23099
    }
23100
  };
23101
 
23102
  /**
23103
   * @ngdoc method
23104
   * @name ngModel.NgModelController#$commitViewValue
23105
   *
23106
   * @description
23107
   * Commit a pending update to the `$modelValue`.
23108
   *
23109
   * Updates may be pending by a debounced event or because the input is waiting for a some future
23110
   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
23111
   * usually handles calling this in response to input events.
23112
   */
23113
  this.$commitViewValue = function() {
23114
    var viewValue = ctrl.$viewValue;
23115
 
23116
    $timeout.cancel(pendingDebounce);
23117
 
23118
    // If the view value has not changed then we should just exit, except in the case where there is
23119
    // a native validator on the element. In this case the validation state may have changed even though
23120
    // the viewValue has stayed empty.
23121
    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
23122
      return;
23123
    }
23124
    ctrl.$$lastCommittedViewValue = viewValue;
23125
 
23126
    // change to dirty
23127
    if (ctrl.$pristine) {
23128
      this.$setDirty();
23129
    }
23130
    this.$$parseAndValidate();
23131
  };
23132
 
23133
  this.$$parseAndValidate = function() {
23134
    var viewValue = ctrl.$$lastCommittedViewValue;
23135
    var modelValue = viewValue;
23136
    var parserValid = isUndefined(modelValue) ? undefined : true;
23137
 
23138
    if (parserValid) {
23139
      for (var i = 0; i < ctrl.$parsers.length; i++) {
23140
        modelValue = ctrl.$parsers[i](modelValue);
23141
        if (isUndefined(modelValue)) {
23142
          parserValid = false;
23143
          break;
23144
        }
23145
      }
23146
    }
23147
    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
23148
      // ctrl.$modelValue has not been touched yet...
23149
      ctrl.$modelValue = ngModelGet($scope);
23150
    }
23151
    var prevModelValue = ctrl.$modelValue;
23152
    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
23153
    ctrl.$$rawModelValue = modelValue;
23154
 
23155
    if (allowInvalid) {
23156
      ctrl.$modelValue = modelValue;
23157
      writeToModelIfNeeded();
23158
    }
23159
 
23160
    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
23161
    // This can happen if e.g. $setViewValue is called from inside a parser
23162
    ctrl.$$runValidators(parserValid, modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
23163
      if (!allowInvalid) {
23164
        // Note: Don't check ctrl.$valid here, as we could have
23165
        // external validators (e.g. calculated on the server),
23166
        // that just call $setValidity and need the model value
23167
        // to calculate their validity.
23168
        ctrl.$modelValue = allValid ? modelValue : undefined;
23169
        writeToModelIfNeeded();
23170
      }
23171
    });
23172
 
23173
    function writeToModelIfNeeded() {
23174
      if (ctrl.$modelValue !== prevModelValue) {
23175
        ctrl.$$writeModelToScope();
23176
      }
23177
    }
23178
  };
23179
 
23180
  this.$$writeModelToScope = function() {
23181
    ngModelSet($scope, ctrl.$modelValue);
23182
    forEach(ctrl.$viewChangeListeners, function(listener) {
23183
      try {
23184
        listener();
23185
      } catch (e) {
23186
        $exceptionHandler(e);
23187
      }
23188
    });
23189
  };
23190
 
23191
  /**
23192
   * @ngdoc method
23193
   * @name ngModel.NgModelController#$setViewValue
23194
   *
23195
   * @description
23196
   * Update the view value.
23197
   *
23198
   * This method should be called when an input directive want to change the view value; typically,
23199
   * this is done from within a DOM event handler.
23200
   *
23201
   * For example {@link ng.directive:input input} calls it when the value of the input changes and
23202
   * {@link ng.directive:select select} calls it when an option is selected.
23203
   *
23204
   * If the new `value` is an object (rather than a string or a number), we should make a copy of the
23205
   * object before passing it to `$setViewValue`.  This is because `ngModel` does not perform a deep
23206
   * watch of objects, it only looks for a change of identity. If you only change the property of
23207
   * the object then ngModel will not realise that the object has changed and will not invoke the
23208
   * `$parsers` and `$validators` pipelines.
23209
   *
23210
   * For this reason, you should not change properties of the copy once it has been passed to
23211
   * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
23212
   *
23213
   * When this method is called, the new `value` will be staged for committing through the `$parsers`
23214
   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
23215
   * value sent directly for processing, finally to be applied to `$modelValue` and then the
23216
   * **expression** specified in the `ng-model` attribute.
23217
   *
23218
   * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
23219
   *
23220
   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
23221
   * and the `default` trigger is not listed, all those actions will remain pending until one of the
23222
   * `updateOn` events is triggered on the DOM element.
23223
   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
23224
   * directive is used with a custom debounce for this particular event.
23225
   *
23226
   * Note that calling this function does not trigger a `$digest`.
23227
   *
23228
   * @param {string} value Value from the view.
23229
   * @param {string} trigger Event that triggered the update.
23230
   */
23231
  this.$setViewValue = function(value, trigger) {
23232
    ctrl.$viewValue = value;
23233
    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
23234
      ctrl.$$debounceViewValueCommit(trigger);
23235
    }
23236
  };
23237
 
23238
  this.$$debounceViewValueCommit = function(trigger) {
23239
    var debounceDelay = 0,
23240
        options = ctrl.$options,
23241
        debounce;
23242
 
23243
    if (options && isDefined(options.debounce)) {
23244
      debounce = options.debounce;
23245
      if (isNumber(debounce)) {
23246
        debounceDelay = debounce;
23247
      } else if (isNumber(debounce[trigger])) {
23248
        debounceDelay = debounce[trigger];
23249
      } else if (isNumber(debounce['default'])) {
23250
        debounceDelay = debounce['default'];
23251
      }
23252
    }
23253
 
23254
    $timeout.cancel(pendingDebounce);
23255
    if (debounceDelay) {
23256
      pendingDebounce = $timeout(function() {
23257
        ctrl.$commitViewValue();
23258
      }, debounceDelay);
23259
    } else if ($rootScope.$$phase) {
23260
      ctrl.$commitViewValue();
23261
    } else {
23262
      $scope.$apply(function() {
23263
        ctrl.$commitViewValue();
23264
      });
23265
    }
23266
  };
23267
 
23268
  // model -> value
23269
  // Note: we cannot use a normal scope.$watch as we want to detect the following:
23270
  // 1. scope value is 'a'
23271
  // 2. user enters 'b'
23272
  // 3. ng-change kicks in and reverts scope value to 'a'
23273
  //    -> scope value did not change since the last digest as
23274
  //       ng-change executes in apply phase
23275
  // 4. view should be changed back to 'a'
23276
  $scope.$watch(function ngModelWatch() {
23277
    var modelValue = ngModelGet($scope);
23278
 
23279
    // if scope model value and ngModel value are out of sync
23280
    // TODO(perf): why not move this to the action fn?
23281
    if (modelValue !== ctrl.$modelValue) {
23282
      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
23283
 
23284
      var formatters = ctrl.$formatters,
23285
          idx = formatters.length;
23286
 
23287
      var viewValue = modelValue;
23288
      while (idx--) {
23289
        viewValue = formatters[idx](viewValue);
23290
      }
23291
      if (ctrl.$viewValue !== viewValue) {
23292
        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
23293
        ctrl.$render();
23294
 
23295
        ctrl.$$runValidators(undefined, modelValue, viewValue, noop);
23296
      }
23297
    }
23298
 
23299
    return modelValue;
23300
  });
23301
}];
23302
 
23303
 
23304
/**
23305
 * @ngdoc directive
23306
 * @name ngModel
23307
 *
23308
 * @element input
23309
 * @priority 1
23310
 *
23311
 * @description
23312
 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
23313
 * property on the scope using {@link ngModel.NgModelController NgModelController},
23314
 * which is created and exposed by this directive.
23315
 *
23316
 * `ngModel` is responsible for:
23317
 *
23318
 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
23319
 *   require.
23320
 * - Providing validation behavior (i.e. required, number, email, url).
23321
 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
23322
 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
23323
 * - Registering the control with its parent {@link ng.directive:form form}.
23324
 *
23325
 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
23326
 * current scope. If the property doesn't already exist on this scope, it will be created
23327
 * implicitly and added to the scope.
23328
 *
23329
 * For best practices on using `ngModel`, see:
23330
 *
23331
 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
23332
 *
23333
 * For basic examples, how to use `ngModel`, see:
23334
 *
23335
 *  - {@link ng.directive:input input}
23336
 *    - {@link input[text] text}
23337
 *    - {@link input[checkbox] checkbox}
23338
 *    - {@link input[radio] radio}
23339
 *    - {@link input[number] number}
23340
 *    - {@link input[email] email}
23341
 *    - {@link input[url] url}
23342
 *    - {@link input[date] date}
23343
 *    - {@link input[datetime-local] datetime-local}
23344
 *    - {@link input[time] time}
23345
 *    - {@link input[month] month}
23346
 *    - {@link input[week] week}
23347
 *  - {@link ng.directive:select select}
23348
 *  - {@link ng.directive:textarea textarea}
23349
 *
23350
 * # CSS classes
23351
 * The following CSS classes are added and removed on the associated input/select/textarea element
23352
 * depending on the validity of the model.
23353
 *
23354
 *  - `ng-valid`: the model is valid
23355
 *  - `ng-invalid`: the model is invalid
23356
 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
23357
 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
23358
 *  - `ng-pristine`: the control hasn't been interacted with yet
23359
 *  - `ng-dirty`: the control has been interacted with
23360
 *  - `ng-touched`: the control has been blurred
23361
 *  - `ng-untouched`: the control hasn't been blurred
23362
 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
23363
 *
23364
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
23365
 *
23366
 * ## Animation Hooks
23367
 *
23368
 * Animations within models are triggered when any of the associated CSS classes are added and removed
23369
 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
23370
 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
23371
 * The animations that are triggered within ngModel are similar to how they work in ngClass and
23372
 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
23373
 *
23374
 * The following example shows a simple way to utilize CSS transitions to style an input element
23375
 * that has been rendered as invalid after it has been validated:
23376
 *
23377
 * <pre>
23378
 * //be sure to include ngAnimate as a module to hook into more
23379
 * //advanced animations
23380
 * .my-input {
23381
 *   transition:0.5s linear all;
23382
 *   background: white;
23383
 * }
23384
 * .my-input.ng-invalid {
23385
 *   background: red;
23386
 *   color:white;
23387
 * }
23388
 * </pre>
23389
 *
23390
 * @example
23391
 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
23392
     <file name="index.html">
23393
       <script>
23394
        angular.module('inputExample', [])
23395
          .controller('ExampleController', ['$scope', function($scope) {
23396
            $scope.val = '1';
23397
          }]);
23398
       </script>
23399
       <style>
23400
         .my-input {
23401
           -webkit-transition:all linear 0.5s;
23402
           transition:all linear 0.5s;
23403
           background: transparent;
23404
         }
23405
         .my-input.ng-invalid {
23406
           color:white;
23407
           background: red;
23408
         }
23409
       </style>
23410
       Update input to see transitions when valid/invalid.
23411
       Integer is a valid value.
23412
       <form name="testForm" ng-controller="ExampleController">
23413
         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
23414
       </form>
23415
     </file>
23416
 * </example>
23417
 *
23418
 * ## Binding to a getter/setter
23419
 *
23420
 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
23421
 * function that returns a representation of the model when called with zero arguments, and sets
23422
 * the internal state of a model when called with an argument. It's sometimes useful to use this
23423
 * for models that have an internal representation that's different than what the model exposes
23424
 * to the view.
23425
 *
23426
 * <div class="alert alert-success">
23427
 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
23428
 * frequently than other parts of your code.
23429
 * </div>
23430
 *
23431
 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
23432
 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
23433
 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
23434
 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
23435
 *
23436
 * The following example shows how to use `ngModel` with a getter/setter:
23437
 *
23438
 * @example
23439
 * <example name="ngModel-getter-setter" module="getterSetterExample">
23440
     <file name="index.html">
23441
       <div ng-controller="ExampleController">
23442
         <form name="userForm">
23443
           Name:
23444
           <input type="text" name="userName"
23445
                  ng-model="user.name"
23446
                  ng-model-options="{ getterSetter: true }" />
23447
         </form>
23448
         <pre>user.name = <span ng-bind="user.name()"></span></pre>
23449
       </div>
23450
     </file>
23451
     <file name="app.js">
23452
       angular.module('getterSetterExample', [])
23453
         .controller('ExampleController', ['$scope', function($scope) {
23454
           var _name = 'Brian';
23455
           $scope.user = {
23456
             name: function(newName) {
23457
               if (angular.isDefined(newName)) {
23458
                 _name = newName;
23459
               }
23460
               return _name;
23461
             }
23462
           };
23463
         }]);
23464
     </file>
23465
 * </example>
23466
 */
23467
var ngModelDirective = ['$rootScope', function($rootScope) {
23468
  return {
23469
    restrict: 'A',
23470
    require: ['ngModel', '^?form', '^?ngModelOptions'],
23471
    controller: NgModelController,
23472
    // Prelink needs to run before any input directive
23473
    // so that we can set the NgModelOptions in NgModelController
23474
    // before anyone else uses it.
23475
    priority: 1,
23476
    compile: function ngModelCompile(element) {
23477
      // Setup initial state of the control
23478
      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
23479
 
23480
      return {
23481
        pre: function ngModelPreLink(scope, element, attr, ctrls) {
23482
          var modelCtrl = ctrls[0],
23483
              formCtrl = ctrls[1] || nullFormCtrl;
23484
 
23485
          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
23486
 
23487
          // notify others, especially parent forms
23488
          formCtrl.$addControl(modelCtrl);
23489
 
23490
          attr.$observe('name', function(newValue) {
23491
            if (modelCtrl.$name !== newValue) {
23492
              formCtrl.$$renameControl(modelCtrl, newValue);
23493
            }
23494
          });
23495
 
23496
          scope.$on('$destroy', function() {
23497
            formCtrl.$removeControl(modelCtrl);
23498
          });
23499
        },
23500
        post: function ngModelPostLink(scope, element, attr, ctrls) {
23501
          var modelCtrl = ctrls[0];
23502
          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
23503
            element.on(modelCtrl.$options.updateOn, function(ev) {
23504
              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
23505
            });
23506
          }
23507
 
23508
          element.on('blur', function(ev) {
23509
            if (modelCtrl.$touched) return;
23510
 
23511
            if ($rootScope.$$phase) {
23512
              scope.$evalAsync(modelCtrl.$setTouched);
23513
            } else {
23514
              scope.$apply(modelCtrl.$setTouched);
23515
            }
23516
          });
23517
        }
23518
      };
23519
    }
23520
  };
23521
}];
23522
 
23523
var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
23524
 
23525
/**
23526
 * @ngdoc directive
23527
 * @name ngModelOptions
23528
 *
23529
 * @description
23530
 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
23531
 * events that will trigger a model update and/or a debouncing delay so that the actual update only
23532
 * takes place when a timer expires; this timer will be reset after another change takes place.
23533
 *
23534
 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
23535
 * be different than the value in the actual model. This means that if you update the model you
23536
 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
23537
 * order to make sure it is synchronized with the model and that any debounced action is canceled.
23538
 *
23539
 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
23540
 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
23541
 * important because `form` controllers are published to the related scope under the name in their
23542
 * `name` attribute.
23543
 *
23544
 * Any pending changes will take place immediately when an enclosing form is submitted via the
23545
 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
23546
 * to have access to the updated model.
23547
 *
23548
 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
23549
 *
23550
 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
23551
 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
23552
 *     events using an space delimited list. There is a special event called `default` that
23553
 *     matches the default events belonging of the control.
23554
 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
23555
 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
23556
 *     custom value for each event. For example:
23557
 *     `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
23558
 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
23559
 *     not validate correctly instead of the default behavior of setting the model to undefined.
23560
 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
23561
       `ngModel` as getters/setters.
23562
 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
23563
 *     `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
23564
 *     otherwise the default timezone of the browser will be used.
23565
 *
23566
 * @example
23567
 
23568
  The following example shows how to override immediate updates. Changes on the inputs within the
23569
  form will update the model only when the control loses focus (blur event). If `escape` key is
23570
  pressed while the input field is focused, the value is reset to the value in the current model.
23571
 
23572
  <example name="ngModelOptions-directive-blur" module="optionsExample">
23573
    <file name="index.html">
23574
      <div ng-controller="ExampleController">
23575
        <form name="userForm">
23576
          Name:
23577
          <input type="text" name="userName"
23578
                 ng-model="user.name"
23579
                 ng-model-options="{ updateOn: 'blur' }"
23580
                 ng-keyup="cancel($event)" /><br />
23581
 
23582
          Other data:
23583
          <input type="text" ng-model="user.data" /><br />
23584
        </form>
23585
        <pre>user.name = <span ng-bind="user.name"></span></pre>
23586
      </div>
23587
    </file>
23588
    <file name="app.js">
23589
      angular.module('optionsExample', [])
23590
        .controller('ExampleController', ['$scope', function($scope) {
23591
          $scope.user = { name: 'say', data: '' };
23592
 
23593
          $scope.cancel = function(e) {
23594
            if (e.keyCode == 27) {
23595
              $scope.userForm.userName.$rollbackViewValue();
23596
            }
23597
          };
23598
        }]);
23599
    </file>
23600
    <file name="protractor.js" type="protractor">
23601
      var model = element(by.binding('user.name'));
23602
      var input = element(by.model('user.name'));
23603
      var other = element(by.model('user.data'));
23604
 
23605
      it('should allow custom events', function() {
23606
        input.sendKeys(' hello');
23607
        input.click();
23608
        expect(model.getText()).toEqual('say');
23609
        other.click();
23610
        expect(model.getText()).toEqual('say hello');
23611
      });
23612
 
23613
      it('should $rollbackViewValue when model changes', function() {
23614
        input.sendKeys(' hello');
23615
        expect(input.getAttribute('value')).toEqual('say hello');
23616
        input.sendKeys(protractor.Key.ESCAPE);
23617
        expect(input.getAttribute('value')).toEqual('say');
23618
        other.click();
23619
        expect(model.getText()).toEqual('say');
23620
      });
23621
    </file>
23622
  </example>
23623
 
23624
  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
23625
  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
23626
 
23627
  <example name="ngModelOptions-directive-debounce" module="optionsExample">
23628
    <file name="index.html">
23629
      <div ng-controller="ExampleController">
23630
        <form name="userForm">
23631
          Name:
23632
          <input type="text" name="userName"
23633
                 ng-model="user.name"
23634
                 ng-model-options="{ debounce: 1000 }" />
23635
          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
23636
        </form>
23637
        <pre>user.name = <span ng-bind="user.name"></span></pre>
23638
      </div>
23639
    </file>
23640
    <file name="app.js">
23641
      angular.module('optionsExample', [])
23642
        .controller('ExampleController', ['$scope', function($scope) {
23643
          $scope.user = { name: 'say' };
23644
        }]);
23645
    </file>
23646
  </example>
23647
 
23648
  This one shows how to bind to getter/setters:
23649
 
23650
  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
23651
    <file name="index.html">
23652
      <div ng-controller="ExampleController">
23653
        <form name="userForm">
23654
          Name:
23655
          <input type="text" name="userName"
23656
                 ng-model="user.name"
23657
                 ng-model-options="{ getterSetter: true }" />
23658
        </form>
23659
        <pre>user.name = <span ng-bind="user.name()"></span></pre>
23660
      </div>
23661
    </file>
23662
    <file name="app.js">
23663
      angular.module('getterSetterExample', [])
23664
        .controller('ExampleController', ['$scope', function($scope) {
23665
          var _name = 'Brian';
23666
          $scope.user = {
23667
            name: function(newName) {
23668
              return angular.isDefined(newName) ? (_name = newName) : _name;
23669
            }
23670
          };
23671
        }]);
23672
    </file>
23673
  </example>
23674
 */
23675
var ngModelOptionsDirective = function() {
23676
  return {
23677
    restrict: 'A',
23678
    controller: ['$scope', '$attrs', function($scope, $attrs) {
23679
      var that = this;
23680
      this.$options = $scope.$eval($attrs.ngModelOptions);
23681
      // Allow adding/overriding bound events
23682
      if (this.$options.updateOn !== undefined) {
23683
        this.$options.updateOnDefault = false;
23684
        // extract "default" pseudo-event from list of events that can trigger a model update
23685
        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
23686
          that.$options.updateOnDefault = true;
23687
          return ' ';
23688
        }));
23689
      } else {
23690
        this.$options.updateOnDefault = true;
23691
      }
23692
    }]
23693
  };
23694
};
23695
 
23696
 
23697
 
23698
// helper methods
23699
function addSetValidityMethod(context) {
23700
  var ctrl = context.ctrl,
23701
      $element = context.$element,
23702
      classCache = {},
23703
      set = context.set,
23704
      unset = context.unset,
23705
      parentForm = context.parentForm,
23706
      $animate = context.$animate;
23707
 
23708
  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
23709
 
23710
  ctrl.$setValidity = setValidity;
23711
 
23712
  function setValidity(validationErrorKey, state, controller) {
23713
    if (state === undefined) {
23714
      createAndSet('$pending', validationErrorKey, controller);
23715
    } else {
23716
      unsetAndCleanup('$pending', validationErrorKey, controller);
23717
    }
23718
    if (!isBoolean(state)) {
23719
      unset(ctrl.$error, validationErrorKey, controller);
23720
      unset(ctrl.$$success, validationErrorKey, controller);
23721
    } else {
23722
      if (state) {
23723
        unset(ctrl.$error, validationErrorKey, controller);
23724
        set(ctrl.$$success, validationErrorKey, controller);
23725
      } else {
23726
        set(ctrl.$error, validationErrorKey, controller);
23727
        unset(ctrl.$$success, validationErrorKey, controller);
23728
      }
23729
    }
23730
    if (ctrl.$pending) {
23731
      cachedToggleClass(PENDING_CLASS, true);
23732
      ctrl.$valid = ctrl.$invalid = undefined;
23733
      toggleValidationCss('', null);
23734
    } else {
23735
      cachedToggleClass(PENDING_CLASS, false);
23736
      ctrl.$valid = isObjectEmpty(ctrl.$error);
23737
      ctrl.$invalid = !ctrl.$valid;
23738
      toggleValidationCss('', ctrl.$valid);
23739
    }
23740
 
23741
    // re-read the state as the set/unset methods could have
23742
    // combined state in ctrl.$error[validationError] (used for forms),
23743
    // where setting/unsetting only increments/decrements the value,
23744
    // and does not replace it.
23745
    var combinedState;
23746
    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
23747
      combinedState = undefined;
23748
    } else if (ctrl.$error[validationErrorKey]) {
23749
      combinedState = false;
23750
    } else if (ctrl.$$success[validationErrorKey]) {
23751
      combinedState = true;
23752
    } else {
23753
      combinedState = null;
23754
    }
23755
 
23756
    toggleValidationCss(validationErrorKey, combinedState);
23757
    parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
23758
  }
23759
 
23760
  function createAndSet(name, value, controller) {
23761
    if (!ctrl[name]) {
23762
      ctrl[name] = {};
23763
    }
23764
    set(ctrl[name], value, controller);
23765
  }
23766
 
23767
  function unsetAndCleanup(name, value, controller) {
23768
    if (ctrl[name]) {
23769
      unset(ctrl[name], value, controller);
23770
    }
23771
    if (isObjectEmpty(ctrl[name])) {
23772
      ctrl[name] = undefined;
23773
    }
23774
  }
23775
 
23776
  function cachedToggleClass(className, switchValue) {
23777
    if (switchValue && !classCache[className]) {
23778
      $animate.addClass($element, className);
23779
      classCache[className] = true;
23780
    } else if (!switchValue && classCache[className]) {
23781
      $animate.removeClass($element, className);
23782
      classCache[className] = false;
23783
    }
23784
  }
23785
 
23786
  function toggleValidationCss(validationErrorKey, isValid) {
23787
    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
23788
 
23789
    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
23790
    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
23791
  }
23792
}
23793
 
23794
function isObjectEmpty(obj) {
23795
  if (obj) {
23796
    for (var prop in obj) {
23797
      return false;
23798
    }
23799
  }
23800
  return true;
23801
}
23802
 
23803
/**
23804
 * @ngdoc directive
23805
 * @name ngNonBindable
23806
 * @restrict AC
23807
 * @priority 1000
23808
 *
23809
 * @description
23810
 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
23811
 * DOM element. This is useful if the element contains what appears to be Angular directives and
23812
 * bindings but which should be ignored by Angular. This could be the case if you have a site that
23813
 * displays snippets of code, for instance.
23814
 *
23815
 * @element ANY
23816
 *
23817
 * @example
23818
 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
23819
 * but the one wrapped in `ngNonBindable` is left alone.
23820
 *
23821
 * @example
23822
    <example>
23823
      <file name="index.html">
23824
        <div>Normal: {{1 + 2}}</div>
23825
        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
23826
      </file>
23827
      <file name="protractor.js" type="protractor">
23828
       it('should check ng-non-bindable', function() {
23829
         expect(element(by.binding('1 + 2')).getText()).toContain('3');
23830
         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
23831
       });
23832
      </file>
23833
    </example>
23834
 */
23835
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
23836
 
23837
/**
23838
 * @ngdoc directive
23839
 * @name ngPluralize
23840
 * @restrict EA
23841
 *
23842
 * @description
23843
 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
23844
 * These rules are bundled with angular.js, but can be overridden
23845
 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
23846
 * by specifying the mappings between
23847
 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23848
 * and the strings to be displayed.
23849
 *
23850
 * # Plural categories and explicit number rules
23851
 * There are two
23852
 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23853
 * in Angular's default en-US locale: "one" and "other".
23854
 *
23855
 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
23856
 * any number that is not 1), an explicit number rule can only match one number. For example, the
23857
 * explicit number rule for "3" matches the number 3. There are examples of plural categories
23858
 * and explicit number rules throughout the rest of this documentation.
23859
 *
23860
 * # Configuring ngPluralize
23861
 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
23862
 * You can also provide an optional attribute, `offset`.
23863
 *
23864
 * The value of the `count` attribute can be either a string or an {@link guide/expression
23865
 * Angular expression}; these are evaluated on the current scope for its bound value.
23866
 *
23867
 * The `when` attribute specifies the mappings between plural categories and the actual
23868
 * string to be displayed. The value of the attribute should be a JSON object.
23869
 *
23870
 * The following example shows how to configure ngPluralize:
23871
 *
23872
 * ```html
23873
 * <ng-pluralize count="personCount"
23874
                 when="{'0': 'Nobody is viewing.',
23875
 *                      'one': '1 person is viewing.',
23876
 *                      'other': '{} people are viewing.'}">
23877
 * </ng-pluralize>
23878
 *```
23879
 *
23880
 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
23881
 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
23882
 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
23883
 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
23884
 * show "a dozen people are viewing".
23885
 *
23886
 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
23887
 * into pluralized strings. In the previous example, Angular will replace `{}` with
23888
 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
23889
 * for <span ng-non-bindable>{{numberExpression}}</span>.
23890
 *
23891
 * # Configuring ngPluralize with offset
23892
 * The `offset` attribute allows further customization of pluralized text, which can result in
23893
 * a better user experience. For example, instead of the message "4 people are viewing this document",
23894
 * you might display "John, Kate and 2 others are viewing this document".
23895
 * The offset attribute allows you to offset a number by any desired value.
23896
 * Let's take a look at an example:
23897
 *
23898
 * ```html
23899
 * <ng-pluralize count="personCount" offset=2
23900
 *               when="{'0': 'Nobody is viewing.',
23901
 *                      '1': '{{person1}} is viewing.',
23902
 *                      '2': '{{person1}} and {{person2}} are viewing.',
23903
 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
23904
 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23905
 * </ng-pluralize>
23906
 * ```
23907
 *
23908
 * Notice that we are still using two plural categories(one, other), but we added
23909
 * three explicit number rules 0, 1 and 2.
23910
 * When one person, perhaps John, views the document, "John is viewing" will be shown.
23911
 * When three people view the document, no explicit number rule is found, so
23912
 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
23913
 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
23914
 * is shown.
23915
 *
23916
 * Note that when you specify offsets, you must provide explicit number rules for
23917
 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
23918
 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
23919
 * plural categories "one" and "other".
23920
 *
23921
 * @param {string|expression} count The variable to be bound to.
23922
 * @param {string} when The mapping between plural category to its corresponding strings.
23923
 * @param {number=} offset Offset to deduct from the total number.
23924
 *
23925
 * @example
23926
    <example module="pluralizeExample">
23927
      <file name="index.html">
23928
        <script>
23929
          angular.module('pluralizeExample', [])
23930
            .controller('ExampleController', ['$scope', function($scope) {
23931
              $scope.person1 = 'Igor';
23932
              $scope.person2 = 'Misko';
23933
              $scope.personCount = 1;
23934
            }]);
23935
        </script>
23936
        <div ng-controller="ExampleController">
23937
          Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
23938
          Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
23939
          Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
23940
 
23941
          <!--- Example with simple pluralization rules for en locale --->
23942
          Without Offset:
23943
          <ng-pluralize count="personCount"
23944
                        when="{'0': 'Nobody is viewing.',
23945
                               'one': '1 person is viewing.',
23946
                               'other': '{} people are viewing.'}">
23947
          </ng-pluralize><br>
23948
 
23949
          <!--- Example with offset --->
23950
          With Offset(2):
23951
          <ng-pluralize count="personCount" offset=2
23952
                        when="{'0': 'Nobody is viewing.',
23953
                               '1': '{{person1}} is viewing.',
23954
                               '2': '{{person1}} and {{person2}} are viewing.',
23955
                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
23956
                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23957
          </ng-pluralize>
23958
        </div>
23959
      </file>
23960
      <file name="protractor.js" type="protractor">
23961
        it('should show correct pluralized string', function() {
23962
          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
23963
          var withOffset = element.all(by.css('ng-pluralize')).get(1);
23964
          var countInput = element(by.model('personCount'));
23965
 
23966
          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
23967
          expect(withOffset.getText()).toEqual('Igor is viewing.');
23968
 
23969
          countInput.clear();
23970
          countInput.sendKeys('0');
23971
 
23972
          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
23973
          expect(withOffset.getText()).toEqual('Nobody is viewing.');
23974
 
23975
          countInput.clear();
23976
          countInput.sendKeys('2');
23977
 
23978
          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
23979
          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
23980
 
23981
          countInput.clear();
23982
          countInput.sendKeys('3');
23983
 
23984
          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
23985
          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
23986
 
23987
          countInput.clear();
23988
          countInput.sendKeys('4');
23989
 
23990
          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
23991
          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
23992
        });
23993
        it('should show data-bound names', function() {
23994
          var withOffset = element.all(by.css('ng-pluralize')).get(1);
23995
          var personCount = element(by.model('personCount'));
23996
          var person1 = element(by.model('person1'));
23997
          var person2 = element(by.model('person2'));
23998
          personCount.clear();
23999
          personCount.sendKeys('4');
24000
          person1.clear();
24001
          person1.sendKeys('Di');
24002
          person2.clear();
24003
          person2.sendKeys('Vojta');
24004
          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
24005
        });
24006
      </file>
24007
    </example>
24008
 */
24009
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
24010
  var BRACE = /{}/g,
24011
      IS_WHEN = /^when(Minus)?(.+)$/;
24012
 
24013
  return {
24014
    restrict: 'EA',
24015
    link: function(scope, element, attr) {
24016
      var numberExp = attr.count,
24017
          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
24018
          offset = attr.offset || 0,
24019
          whens = scope.$eval(whenExp) || {},
24020
          whensExpFns = {},
24021
          startSymbol = $interpolate.startSymbol(),
24022
          endSymbol = $interpolate.endSymbol(),
24023
          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
24024
          watchRemover = angular.noop,
24025
          lastCount;
24026
 
24027
      forEach(attr, function(expression, attributeName) {
24028
        var tmpMatch = IS_WHEN.exec(attributeName);
24029
        if (tmpMatch) {
24030
          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
24031
          whens[whenKey] = element.attr(attr.$attr[attributeName]);
24032
        }
24033
      });
24034
      forEach(whens, function(expression, key) {
24035
        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
24036
 
24037
      });
24038
 
24039
      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
24040
        var count = parseFloat(newVal);
24041
        var countIsNaN = isNaN(count);
24042
 
24043
        if (!countIsNaN && !(count in whens)) {
24044
          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
24045
          // Otherwise, check it against pluralization rules in $locale service.
24046
          count = $locale.pluralCat(count - offset);
24047
        }
24048
 
24049
        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
24050
        // In JS `NaN !== NaN`, so we have to exlicitly check.
24051
        if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
24052
          watchRemover();
24053
          watchRemover = scope.$watch(whensExpFns[count], updateElementText);
24054
          lastCount = count;
24055
        }
24056
      });
24057
 
24058
      function updateElementText(newText) {
24059
        element.text(newText || '');
24060
      }
24061
    }
24062
  };
24063
}];
24064
 
24065
/**
24066
 * @ngdoc directive
24067
 * @name ngRepeat
24068
 *
24069
 * @description
24070
 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
24071
 * instance gets its own scope, where the given loop variable is set to the current collection item,
24072
 * and `$index` is set to the item index or key.
24073
 *
24074
 * Special properties are exposed on the local scope of each template instance, including:
24075
 *
24076
 * | Variable  | Type            | Details                                                                     |
24077
 * |-----------|-----------------|-----------------------------------------------------------------------------|
24078
 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
24079
 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
24080
 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
24081
 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
24082
 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
24083
 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
24084
 *
24085
 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
24086
 * This may be useful when, for instance, nesting ngRepeats.
24087
 *
24088
 * # Iterating over object properties
24089
 *
24090
 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
24091
 * syntax:
24092
 *
24093
 * ```js
24094
 * <div ng-repeat="(key, value) in myObj"> ... </div>
24095
 * ```
24096
 *
24097
 * You need to be aware that the JavaScript specification does not define what order
24098
 * it will return the keys for an object. In order to have a guaranteed deterministic order
24099
 * for the keys, Angular versions up to and including 1.3 **sort the keys alphabetically**.
24100
 *
24101
 * If this is not desired, the recommended workaround is to convert your object into an array
24102
 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
24103
 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
24104
 * or implement a `$watch` on the object yourself.
24105
 *
24106
 * In version 1.4 we will remove the sorting, since it seems that browsers generally follow the
24107
 * strategy of providing keys in the order in which they were defined, although there are exceptions
24108
 * when keys are deleted and reinstated.
24109
 *
24110
 *
24111
 * # Special repeat start and end points
24112
 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
24113
 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
24114
 * 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)
24115
 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
24116
 *
24117
 * The example below makes use of this feature:
24118
 * ```html
24119
 *   <header ng-repeat-start="item in items">
24120
 *     Header {{ item }}
24121
 *   </header>
24122
 *   <div class="body">
24123
 *     Body {{ item }}
24124
 *   </div>
24125
 *   <footer ng-repeat-end>
24126
 *     Footer {{ item }}
24127
 *   </footer>
24128
 * ```
24129
 *
24130
 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
24131
 * ```html
24132
 *   <header>
24133
 *     Header A
24134
 *   </header>
24135
 *   <div class="body">
24136
 *     Body A
24137
 *   </div>
24138
 *   <footer>
24139
 *     Footer A
24140
 *   </footer>
24141
 *   <header>
24142
 *     Header B
24143
 *   </header>
24144
 *   <div class="body">
24145
 *     Body B
24146
 *   </div>
24147
 *   <footer>
24148
 *     Footer B
24149
 *   </footer>
24150
 * ```
24151
 *
24152
 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
24153
 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
24154
 *
24155
 * @animations
24156
 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
24157
 *
24158
 * **.leave** - when an item is removed from the list or when an item is filtered out
24159
 *
24160
 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
24161
 *
24162
 * @element ANY
24163
 * @scope
24164
 * @priority 1000
24165
 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
24166
 *   formats are currently supported:
24167
 *
24168
 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
24169
 *     is a scope expression giving the collection to enumerate.
24170
 *
24171
 *     For example: `album in artist.albums`.
24172
 *
24173
 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
24174
 *     and `expression` is the scope expression giving the collection to enumerate.
24175
 *
24176
 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
24177
 *
24178
 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
24179
 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking function
24180
 *     is specified the ng-repeat associates elements by identity in the collection. It is an error to have
24181
 *     more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
24182
 *     mapped to the same DOM element, which is not possible.)  Filters should be applied to the expression,
24183
 *     before specifying a tracking expression.
24184
 *
24185
 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
24186
 *     will be associated by item identity in the array.
24187
 *
24188
 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
24189
 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
24190
 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
24191
 *     element in the same way in the DOM.
24192
 *
24193
 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
24194
 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
24195
 *     property is same.
24196
 *
24197
 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
24198
 *     to items in conjunction with a tracking expression.
24199
 *
24200
 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
24201
 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
24202
 *     when a filter is active on the repeater, but the filtered result set is empty.
24203
 *
24204
 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
24205
 *     the items have been processed through the filter.
24206
 *
24207
 * @example
24208
 * This example initializes the scope to a list of names and
24209
 * then uses `ngRepeat` to display every person:
24210
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24211
    <file name="index.html">
24212
      <div ng-init="friends = [
24213
        {name:'John', age:25, gender:'boy'},
24214
        {name:'Jessie', age:30, gender:'girl'},
24215
        {name:'Johanna', age:28, gender:'girl'},
24216
        {name:'Joy', age:15, gender:'girl'},
24217
        {name:'Mary', age:28, gender:'girl'},
24218
        {name:'Peter', age:95, gender:'boy'},
24219
        {name:'Sebastian', age:50, gender:'boy'},
24220
        {name:'Erika', age:27, gender:'girl'},
24221
        {name:'Patrick', age:40, gender:'boy'},
24222
        {name:'Samantha', age:60, gender:'girl'}
24223
      ]">
24224
        I have {{friends.length}} friends. They are:
24225
        <input type="search" ng-model="q" placeholder="filter friends..." />
24226
        <ul class="example-animate-container">
24227
          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
24228
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
24229
          </li>
24230
          <li class="animate-repeat" ng-if="results.length == 0">
24231
            <strong>No results found...</strong>
24232
          </li>
24233
        </ul>
24234
      </div>
24235
    </file>
24236
    <file name="animations.css">
24237
      .example-animate-container {
24238
        background:white;
24239
        border:1px solid black;
24240
        list-style:none;
24241
        margin:0;
24242
        padding:0 10px;
24243
      }
24244
 
24245
      .animate-repeat {
24246
        line-height:40px;
24247
        list-style:none;
24248
        box-sizing:border-box;
24249
      }
24250
 
24251
      .animate-repeat.ng-move,
24252
      .animate-repeat.ng-enter,
24253
      .animate-repeat.ng-leave {
24254
        -webkit-transition:all linear 0.5s;
24255
        transition:all linear 0.5s;
24256
      }
24257
 
24258
      .animate-repeat.ng-leave.ng-leave-active,
24259
      .animate-repeat.ng-move,
24260
      .animate-repeat.ng-enter {
24261
        opacity:0;
24262
        max-height:0;
24263
      }
24264
 
24265
      .animate-repeat.ng-leave,
24266
      .animate-repeat.ng-move.ng-move-active,
24267
      .animate-repeat.ng-enter.ng-enter-active {
24268
        opacity:1;
24269
        max-height:40px;
24270
      }
24271
    </file>
24272
    <file name="protractor.js" type="protractor">
24273
      var friends = element.all(by.repeater('friend in friends'));
24274
 
24275
      it('should render initial data set', function() {
24276
        expect(friends.count()).toBe(10);
24277
        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
24278
        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
24279
        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
24280
        expect(element(by.binding('friends.length')).getText())
24281
            .toMatch("I have 10 friends. They are:");
24282
      });
24283
 
24284
       it('should update repeater when filter predicate changes', function() {
24285
         expect(friends.count()).toBe(10);
24286
 
24287
         element(by.model('q')).sendKeys('ma');
24288
 
24289
         expect(friends.count()).toBe(2);
24290
         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
24291
         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
24292
       });
24293
      </file>
24294
    </example>
24295
 */
24296
var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24297
  var NG_REMOVED = '$$NG_REMOVED';
24298
  var ngRepeatMinErr = minErr('ngRepeat');
24299
 
24300
  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
24301
    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
24302
    scope[valueIdentifier] = value;
24303
    if (keyIdentifier) scope[keyIdentifier] = key;
24304
    scope.$index = index;
24305
    scope.$first = (index === 0);
24306
    scope.$last = (index === (arrayLength - 1));
24307
    scope.$middle = !(scope.$first || scope.$last);
24308
    // jshint bitwise: false
24309
    scope.$odd = !(scope.$even = (index&1) === 0);
24310
    // jshint bitwise: true
24311
  };
24312
 
24313
  var getBlockStart = function(block) {
24314
    return block.clone[0];
24315
  };
24316
 
24317
  var getBlockEnd = function(block) {
24318
    return block.clone[block.clone.length - 1];
24319
  };
24320
 
24321
 
24322
  return {
24323
    restrict: 'A',
24324
    multiElement: true,
24325
    transclude: 'element',
24326
    priority: 1000,
24327
    terminal: true,
24328
    $$tlb: true,
24329
    compile: function ngRepeatCompile($element, $attr) {
24330
      var expression = $attr.ngRepeat;
24331
      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
24332
 
24333
      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*$/);
24334
 
24335
      if (!match) {
24336
        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
24337
            expression);
24338
      }
24339
 
24340
      var lhs = match[1];
24341
      var rhs = match[2];
24342
      var aliasAs = match[3];
24343
      var trackByExp = match[4];
24344
 
24345
      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
24346
 
24347
      if (!match) {
24348
        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
24349
            lhs);
24350
      }
24351
      var valueIdentifier = match[3] || match[1];
24352
      var keyIdentifier = match[2];
24353
 
24354
      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
24355
          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
24356
        throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
24357
          aliasAs);
24358
      }
24359
 
24360
      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
24361
      var hashFnLocals = {$id: hashKey};
24362
 
24363
      if (trackByExp) {
24364
        trackByExpGetter = $parse(trackByExp);
24365
      } else {
24366
        trackByIdArrayFn = function(key, value) {
24367
          return hashKey(value);
24368
        };
24369
        trackByIdObjFn = function(key) {
24370
          return key;
24371
        };
24372
      }
24373
 
24374
      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
24375
 
24376
        if (trackByExpGetter) {
24377
          trackByIdExpFn = function(key, value, index) {
24378
            // assign key, value, and $index to the locals so that they can be used in hash functions
24379
            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24380
            hashFnLocals[valueIdentifier] = value;
24381
            hashFnLocals.$index = index;
24382
            return trackByExpGetter($scope, hashFnLocals);
24383
          };
24384
        }
24385
 
24386
        // Store a list of elements from previous run. This is a hash where key is the item from the
24387
        // iterator, and the value is objects with following properties.
24388
        //   - scope: bound scope
24389
        //   - element: previous element.
24390
        //   - index: position
24391
        //
24392
        // We are using no-proto object so that we don't need to guard against inherited props via
24393
        // hasOwnProperty.
24394
        var lastBlockMap = createMap();
24395
 
24396
        //watch props
24397
        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
24398
          var index, length,
24399
              previousNode = $element[0],     // node that cloned nodes should be inserted after
24400
                                              // initialized to the comment node anchor
24401
              nextNode,
24402
              // Same as lastBlockMap but it has the current state. It will become the
24403
              // lastBlockMap on the next iteration.
24404
              nextBlockMap = createMap(),
24405
              collectionLength,
24406
              key, value, // key/value of iteration
24407
              trackById,
24408
              trackByIdFn,
24409
              collectionKeys,
24410
              block,       // last object information {scope, element, id}
24411
              nextBlockOrder,
24412
              elementsToRemove;
24413
 
24414
          if (aliasAs) {
24415
            $scope[aliasAs] = collection;
24416
          }
24417
 
24418
          if (isArrayLike(collection)) {
24419
            collectionKeys = collection;
24420
            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
24421
          } else {
24422
            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
24423
            // if object, extract keys, sort them and use to determine order of iteration over obj props
24424
            collectionKeys = [];
24425
            for (var itemKey in collection) {
24426
              if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
24427
                collectionKeys.push(itemKey);
24428
              }
24429
            }
24430
            collectionKeys.sort();
24431
          }
24432
 
24433
          collectionLength = collectionKeys.length;
24434
          nextBlockOrder = new Array(collectionLength);
24435
 
24436
          // locate existing items
24437
          for (index = 0; index < collectionLength; index++) {
24438
            key = (collection === collectionKeys) ? index : collectionKeys[index];
24439
            value = collection[key];
24440
            trackById = trackByIdFn(key, value, index);
24441
            if (lastBlockMap[trackById]) {
24442
              // found previously seen block
24443
              block = lastBlockMap[trackById];
24444
              delete lastBlockMap[trackById];
24445
              nextBlockMap[trackById] = block;
24446
              nextBlockOrder[index] = block;
24447
            } else if (nextBlockMap[trackById]) {
24448
              // if collision detected. restore lastBlockMap and throw an error
24449
              forEach(nextBlockOrder, function(block) {
24450
                if (block && block.scope) lastBlockMap[block.id] = block;
24451
              });
24452
              throw ngRepeatMinErr('dupes',
24453
                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24454
                  expression, trackById, value);
24455
            } else {
24456
              // new never before seen block
24457
              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
24458
              nextBlockMap[trackById] = true;
24459
            }
24460
          }
24461
 
24462
          // remove leftover items
24463
          for (var blockKey in lastBlockMap) {
24464
            block = lastBlockMap[blockKey];
24465
            elementsToRemove = getBlockNodes(block.clone);
24466
            $animate.leave(elementsToRemove);
24467
            if (elementsToRemove[0].parentNode) {
24468
              // if the element was not removed yet because of pending animation, mark it as deleted
24469
              // so that we can ignore it later
24470
              for (index = 0, length = elementsToRemove.length; index < length; index++) {
24471
                elementsToRemove[index][NG_REMOVED] = true;
24472
              }
24473
            }
24474
            block.scope.$destroy();
24475
          }
24476
 
24477
          // we are not using forEach for perf reasons (trying to avoid #call)
24478
          for (index = 0; index < collectionLength; index++) {
24479
            key = (collection === collectionKeys) ? index : collectionKeys[index];
24480
            value = collection[key];
24481
            block = nextBlockOrder[index];
24482
 
24483
            if (block.scope) {
24484
              // if we have already seen this object, then we need to reuse the
24485
              // associated scope/element
24486
 
24487
              nextNode = previousNode;
24488
 
24489
              // skip nodes that are already pending removal via leave animation
24490
              do {
24491
                nextNode = nextNode.nextSibling;
24492
              } while (nextNode && nextNode[NG_REMOVED]);
24493
 
24494
              if (getBlockStart(block) != nextNode) {
24495
                // existing item which got moved
24496
                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
24497
              }
24498
              previousNode = getBlockEnd(block);
24499
              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24500
            } else {
24501
              // new item which we don't know about
24502
              $transclude(function ngRepeatTransclude(clone, scope) {
24503
                block.scope = scope;
24504
                // http://jsperf.com/clone-vs-createcomment
24505
                var endNode = ngRepeatEndComment.cloneNode(false);
24506
                clone[clone.length++] = endNode;
24507
 
24508
                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
24509
                $animate.enter(clone, null, jqLite(previousNode));
24510
                previousNode = endNode;
24511
                // Note: We only need the first/last node of the cloned nodes.
24512
                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24513
                // by a directive with templateUrl when its template arrives.
24514
                block.clone = clone;
24515
                nextBlockMap[block.id] = block;
24516
                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24517
              });
24518
            }
24519
          }
24520
          lastBlockMap = nextBlockMap;
24521
        });
24522
      };
24523
    }
24524
  };
24525
}];
24526
 
24527
var NG_HIDE_CLASS = 'ng-hide';
24528
var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24529
/**
24530
 * @ngdoc directive
24531
 * @name ngShow
24532
 *
24533
 * @description
24534
 * The `ngShow` directive shows or hides the given HTML element based on the expression
24535
 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
24536
 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24537
 * in AngularJS and sets the display style to none (using an !important flag).
24538
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24539
 *
24540
 * ```html
24541
 * <!-- when $scope.myValue is truthy (element is visible) -->
24542
 * <div ng-show="myValue"></div>
24543
 *
24544
 * <!-- when $scope.myValue is falsy (element is hidden) -->
24545
 * <div ng-show="myValue" class="ng-hide"></div>
24546
 * ```
24547
 *
24548
 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
24549
 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
24550
 * from the element causing the element not to appear hidden.
24551
 *
24552
 * ## Why is !important used?
24553
 *
24554
 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24555
 * can be easily overridden by heavier selectors. For example, something as simple
24556
 * as changing the display style on a HTML list item would make hidden elements appear visible.
24557
 * This also becomes a bigger issue when dealing with CSS frameworks.
24558
 *
24559
 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24560
 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24561
 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24562
 *
24563
 * ### Overriding `.ng-hide`
24564
 *
24565
 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24566
 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24567
 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
24568
 * with extra animation classes that can be added.
24569
 *
24570
 * ```css
24571
 * .ng-hide:not(.ng-hide-animate) {
24572
 *   /&#42; this is just another form of hiding an element &#42;/
24573
 *   display: block!important;
24574
 *   position: absolute;
24575
 *   top: -9999px;
24576
 *   left: -9999px;
24577
 * }
24578
 * ```
24579
 *
24580
 * By default you don't need to override in CSS anything and the animations will work around the display style.
24581
 *
24582
 * ## A note about animations with `ngShow`
24583
 *
24584
 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24585
 * is true and false. This system works like the animation system present with ngClass except that
24586
 * you must also include the !important flag to override the display property
24587
 * so that you can perform an animation when the element is hidden during the time of the animation.
24588
 *
24589
 * ```css
24590
 * //
24591
 * //a working example can be found at the bottom of this page
24592
 * //
24593
 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24594
 *   /&#42; this is required as of 1.3x to properly
24595
 *      apply all styling in a show/hide animation &#42;/
24596
 *   transition: 0s linear all;
24597
 * }
24598
 *
24599
 * .my-element.ng-hide-add-active,
24600
 * .my-element.ng-hide-remove-active {
24601
 *   /&#42; the transition is defined in the active class &#42;/
24602
 *   transition: 1s linear all;
24603
 * }
24604
 *
24605
 * .my-element.ng-hide-add { ... }
24606
 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24607
 * .my-element.ng-hide-remove { ... }
24608
 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24609
 * ```
24610
 *
24611
 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24612
 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24613
 *
24614
 * @animations
24615
 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
24616
 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
24617
 *
24618
 * @element ANY
24619
 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
24620
 *     then the element is shown or hidden respectively.
24621
 *
24622
 * @example
24623
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24624
    <file name="index.html">
24625
      Click me: <input type="checkbox" ng-model="checked"><br/>
24626
      <div>
24627
        Show:
24628
        <div class="check-element animate-show" ng-show="checked">
24629
          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24630
        </div>
24631
      </div>
24632
      <div>
24633
        Hide:
24634
        <div class="check-element animate-show" ng-hide="checked">
24635
          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24636
        </div>
24637
      </div>
24638
    </file>
24639
    <file name="glyphicons.css">
24640
      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24641
    </file>
24642
    <file name="animations.css">
24643
      .animate-show {
24644
        line-height: 20px;
24645
        opacity: 1;
24646
        padding: 10px;
24647
        border: 1px solid black;
24648
        background: white;
24649
      }
24650
 
24651
      .animate-show.ng-hide-add.ng-hide-add-active,
24652
      .animate-show.ng-hide-remove.ng-hide-remove-active {
24653
        -webkit-transition: all linear 0.5s;
24654
        transition: all linear 0.5s;
24655
      }
24656
 
24657
      .animate-show.ng-hide {
24658
        line-height: 0;
24659
        opacity: 0;
24660
        padding: 0 10px;
24661
      }
24662
 
24663
      .check-element {
24664
        padding: 10px;
24665
        border: 1px solid black;
24666
        background: white;
24667
      }
24668
    </file>
24669
    <file name="protractor.js" type="protractor">
24670
      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24671
      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24672
 
24673
      it('should check ng-show / ng-hide', function() {
24674
        expect(thumbsUp.isDisplayed()).toBeFalsy();
24675
        expect(thumbsDown.isDisplayed()).toBeTruthy();
24676
 
24677
        element(by.model('checked')).click();
24678
 
24679
        expect(thumbsUp.isDisplayed()).toBeTruthy();
24680
        expect(thumbsDown.isDisplayed()).toBeFalsy();
24681
      });
24682
    </file>
24683
  </example>
24684
 */
24685
var ngShowDirective = ['$animate', function($animate) {
24686
  return {
24687
    restrict: 'A',
24688
    multiElement: true,
24689
    link: function(scope, element, attr) {
24690
      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
24691
        // we're adding a temporary, animation-specific class for ng-hide since this way
24692
        // we can control when the element is actually displayed on screen without having
24693
        // to have a global/greedy CSS selector that breaks when other animations are run.
24694
        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
24695
        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
24696
          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24697
        });
24698
      });
24699
    }
24700
  };
24701
}];
24702
 
24703
 
24704
/**
24705
 * @ngdoc directive
24706
 * @name ngHide
24707
 *
24708
 * @description
24709
 * The `ngHide` directive shows or hides the given HTML element based on the expression
24710
 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
24711
 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24712
 * in AngularJS and sets the display style to none (using an !important flag).
24713
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24714
 *
24715
 * ```html
24716
 * <!-- when $scope.myValue is truthy (element is hidden) -->
24717
 * <div ng-hide="myValue" class="ng-hide"></div>
24718
 *
24719
 * <!-- when $scope.myValue is falsy (element is visible) -->
24720
 * <div ng-hide="myValue"></div>
24721
 * ```
24722
 *
24723
 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
24724
 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
24725
 * from the element causing the element not to appear hidden.
24726
 *
24727
 * ## Why is !important used?
24728
 *
24729
 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24730
 * can be easily overridden by heavier selectors. For example, something as simple
24731
 * as changing the display style on a HTML list item would make hidden elements appear visible.
24732
 * This also becomes a bigger issue when dealing with CSS frameworks.
24733
 *
24734
 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24735
 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24736
 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24737
 *
24738
 * ### Overriding `.ng-hide`
24739
 *
24740
 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24741
 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24742
 * class in CSS:
24743
 *
24744
 * ```css
24745
 * .ng-hide {
24746
 *   /&#42; this is just another form of hiding an element &#42;/
24747
 *   display: block!important;
24748
 *   position: absolute;
24749
 *   top: -9999px;
24750
 *   left: -9999px;
24751
 * }
24752
 * ```
24753
 *
24754
 * By default you don't need to override in CSS anything and the animations will work around the display style.
24755
 *
24756
 * ## A note about animations with `ngHide`
24757
 *
24758
 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24759
 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
24760
 * CSS class is added and removed for you instead of your own CSS class.
24761
 *
24762
 * ```css
24763
 * //
24764
 * //a working example can be found at the bottom of this page
24765
 * //
24766
 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24767
 *   transition: 0.5s linear all;
24768
 * }
24769
 *
24770
 * .my-element.ng-hide-add { ... }
24771
 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24772
 * .my-element.ng-hide-remove { ... }
24773
 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24774
 * ```
24775
 *
24776
 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24777
 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24778
 *
24779
 * @animations
24780
 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
24781
 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
24782
 *
24783
 * @element ANY
24784
 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
24785
 *     the element is shown or hidden respectively.
24786
 *
24787
 * @example
24788
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24789
    <file name="index.html">
24790
      Click me: <input type="checkbox" ng-model="checked"><br/>
24791
      <div>
24792
        Show:
24793
        <div class="check-element animate-hide" ng-show="checked">
24794
          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24795
        </div>
24796
      </div>
24797
      <div>
24798
        Hide:
24799
        <div class="check-element animate-hide" ng-hide="checked">
24800
          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24801
        </div>
24802
      </div>
24803
    </file>
24804
    <file name="glyphicons.css">
24805
      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24806
    </file>
24807
    <file name="animations.css">
24808
      .animate-hide {
24809
        -webkit-transition: all linear 0.5s;
24810
        transition: all linear 0.5s;
24811
        line-height: 20px;
24812
        opacity: 1;
24813
        padding: 10px;
24814
        border: 1px solid black;
24815
        background: white;
24816
      }
24817
 
24818
      .animate-hide.ng-hide {
24819
        line-height: 0;
24820
        opacity: 0;
24821
        padding: 0 10px;
24822
      }
24823
 
24824
      .check-element {
24825
        padding: 10px;
24826
        border: 1px solid black;
24827
        background: white;
24828
      }
24829
    </file>
24830
    <file name="protractor.js" type="protractor">
24831
      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24832
      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24833
 
24834
      it('should check ng-show / ng-hide', function() {
24835
        expect(thumbsUp.isDisplayed()).toBeFalsy();
24836
        expect(thumbsDown.isDisplayed()).toBeTruthy();
24837
 
24838
        element(by.model('checked')).click();
24839
 
24840
        expect(thumbsUp.isDisplayed()).toBeTruthy();
24841
        expect(thumbsDown.isDisplayed()).toBeFalsy();
24842
      });
24843
    </file>
24844
  </example>
24845
 */
24846
var ngHideDirective = ['$animate', function($animate) {
24847
  return {
24848
    restrict: 'A',
24849
    multiElement: true,
24850
    link: function(scope, element, attr) {
24851
      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
24852
        // The comment inside of the ngShowDirective explains why we add and
24853
        // remove a temporary class for the show/hide animation
24854
        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
24855
          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24856
        });
24857
      });
24858
    }
24859
  };
24860
}];
24861
 
24862
/**
24863
 * @ngdoc directive
24864
 * @name ngStyle
24865
 * @restrict AC
24866
 *
24867
 * @description
24868
 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
24869
 *
24870
 * @element ANY
24871
 * @param {expression} ngStyle
24872
 *
24873
 * {@link guide/expression Expression} which evals to an
24874
 * object whose keys are CSS style names and values are corresponding values for those CSS
24875
 * keys.
24876
 *
24877
 * Since some CSS style names are not valid keys for an object, they must be quoted.
24878
 * See the 'background-color' style in the example below.
24879
 *
24880
 * @example
24881
   <example>
24882
     <file name="index.html">
24883
        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
24884
        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
24885
        <input type="button" value="clear" ng-click="myStyle={}">
24886
        <br/>
24887
        <span ng-style="myStyle">Sample Text</span>
24888
        <pre>myStyle={{myStyle}}</pre>
24889
     </file>
24890
     <file name="style.css">
24891
       span {
24892
         color: black;
24893
       }
24894
     </file>
24895
     <file name="protractor.js" type="protractor">
24896
       var colorSpan = element(by.css('span'));
24897
 
24898
       it('should check ng-style', function() {
24899
         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24900
         element(by.css('input[value=\'set color\']')).click();
24901
         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
24902
         element(by.css('input[value=clear]')).click();
24903
         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24904
       });
24905
     </file>
24906
   </example>
24907
 */
24908
var ngStyleDirective = ngDirective(function(scope, element, attr) {
24909
  scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
24910
    if (oldStyles && (newStyles !== oldStyles)) {
24911
      forEach(oldStyles, function(val, style) { element.css(style, '');});
24912
    }
24913
    if (newStyles) element.css(newStyles);
24914
  });
24915
});
24916
 
24917
/**
24918
 * @ngdoc directive
24919
 * @name ngSwitch
24920
 * @restrict EA
24921
 *
24922
 * @description
24923
 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
24924
 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
24925
 * as specified in the template.
24926
 *
24927
 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
24928
 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
24929
 * matches the value obtained from the evaluated expression. In other words, you define a container element
24930
 * (where you place the directive), place an expression on the **`on="..."` attribute**
24931
 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
24932
 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
24933
 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
24934
 * attribute is displayed.
24935
 *
24936
 * <div class="alert alert-info">
24937
 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
24938
 * as literal string values to match against.
24939
 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
24940
 * value of the expression `$scope.someVal`.
24941
 * </div>
24942
 
24943
 * @animations
24944
 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
24945
 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
24946
 *
24947
 * @usage
24948
 *
24949
 * ```
24950
 * <ANY ng-switch="expression">
24951
 *   <ANY ng-switch-when="matchValue1">...</ANY>
24952
 *   <ANY ng-switch-when="matchValue2">...</ANY>
24953
 *   <ANY ng-switch-default>...</ANY>
24954
 * </ANY>
24955
 * ```
24956
 *
24957
 *
24958
 * @scope
24959
 * @priority 1200
24960
 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
24961
 * On child elements add:
24962
 *
24963
 * * `ngSwitchWhen`: the case statement to match against. If match then this
24964
 *   case will be displayed. If the same match appears multiple times, all the
24965
 *   elements will be displayed.
24966
 * * `ngSwitchDefault`: the default case when no other case match. If there
24967
 *   are multiple default cases, all of them will be displayed when no other
24968
 *   case match.
24969
 *
24970
 *
24971
 * @example
24972
  <example module="switchExample" deps="angular-animate.js" animations="true">
24973
    <file name="index.html">
24974
      <div ng-controller="ExampleController">
24975
        <select ng-model="selection" ng-options="item for item in items">
24976
        </select>
24977
        <tt>selection={{selection}}</tt>
24978
        <hr/>
24979
        <div class="animate-switch-container"
24980
          ng-switch on="selection">
24981
            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
24982
            <div class="animate-switch" ng-switch-when="home">Home Span</div>
24983
            <div class="animate-switch" ng-switch-default>default</div>
24984
        </div>
24985
      </div>
24986
    </file>
24987
    <file name="script.js">
24988
      angular.module('switchExample', ['ngAnimate'])
24989
        .controller('ExampleController', ['$scope', function($scope) {
24990
          $scope.items = ['settings', 'home', 'other'];
24991
          $scope.selection = $scope.items[0];
24992
        }]);
24993
    </file>
24994
    <file name="animations.css">
24995
      .animate-switch-container {
24996
        position:relative;
24997
        background:white;
24998
        border:1px solid black;
24999
        height:40px;
25000
        overflow:hidden;
25001
      }
25002
 
25003
      .animate-switch {
25004
        padding:10px;
25005
      }
25006
 
25007
      .animate-switch.ng-animate {
25008
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25009
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25010
 
25011
        position:absolute;
25012
        top:0;
25013
        left:0;
25014
        right:0;
25015
        bottom:0;
25016
      }
25017
 
25018
      .animate-switch.ng-leave.ng-leave-active,
25019
      .animate-switch.ng-enter {
25020
        top:-50px;
25021
      }
25022
      .animate-switch.ng-leave,
25023
      .animate-switch.ng-enter.ng-enter-active {
25024
        top:0;
25025
      }
25026
    </file>
25027
    <file name="protractor.js" type="protractor">
25028
      var switchElem = element(by.css('[ng-switch]'));
25029
      var select = element(by.model('selection'));
25030
 
25031
      it('should start in settings', function() {
25032
        expect(switchElem.getText()).toMatch(/Settings Div/);
25033
      });
25034
      it('should change to home', function() {
25035
        select.all(by.css('option')).get(1).click();
25036
        expect(switchElem.getText()).toMatch(/Home Span/);
25037
      });
25038
      it('should select default', function() {
25039
        select.all(by.css('option')).get(2).click();
25040
        expect(switchElem.getText()).toMatch(/default/);
25041
      });
25042
    </file>
25043
  </example>
25044
 */
25045
var ngSwitchDirective = ['$animate', function($animate) {
25046
  return {
25047
    restrict: 'EA',
25048
    require: 'ngSwitch',
25049
 
25050
    // asks for $scope to fool the BC controller module
25051
    controller: ['$scope', function ngSwitchController() {
25052
     this.cases = {};
25053
    }],
25054
    link: function(scope, element, attr, ngSwitchController) {
25055
      var watchExpr = attr.ngSwitch || attr.on,
25056
          selectedTranscludes = [],
25057
          selectedElements = [],
25058
          previousLeaveAnimations = [],
25059
          selectedScopes = [];
25060
 
25061
      var spliceFactory = function(array, index) {
25062
          return function() { array.splice(index, 1); };
25063
      };
25064
 
25065
      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
25066
        var i, ii;
25067
        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
25068
          $animate.cancel(previousLeaveAnimations[i]);
25069
        }
25070
        previousLeaveAnimations.length = 0;
25071
 
25072
        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
25073
          var selected = getBlockNodes(selectedElements[i].clone);
25074
          selectedScopes[i].$destroy();
25075
          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
25076
          promise.then(spliceFactory(previousLeaveAnimations, i));
25077
        }
25078
 
25079
        selectedElements.length = 0;
25080
        selectedScopes.length = 0;
25081
 
25082
        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
25083
          forEach(selectedTranscludes, function(selectedTransclude) {
25084
            selectedTransclude.transclude(function(caseElement, selectedScope) {
25085
              selectedScopes.push(selectedScope);
25086
              var anchor = selectedTransclude.element;
25087
              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
25088
              var block = { clone: caseElement };
25089
 
25090
              selectedElements.push(block);
25091
              $animate.enter(caseElement, anchor.parent(), anchor);
25092
            });
25093
          });
25094
        }
25095
      });
25096
    }
25097
  };
25098
}];
25099
 
25100
var ngSwitchWhenDirective = ngDirective({
25101
  transclude: 'element',
25102
  priority: 1200,
25103
  require: '^ngSwitch',
25104
  multiElement: true,
25105
  link: function(scope, element, attrs, ctrl, $transclude) {
25106
    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
25107
    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
25108
  }
25109
});
25110
 
25111
var ngSwitchDefaultDirective = ngDirective({
25112
  transclude: 'element',
25113
  priority: 1200,
25114
  require: '^ngSwitch',
25115
  multiElement: true,
25116
  link: function(scope, element, attr, ctrl, $transclude) {
25117
    ctrl.cases['?'] = (ctrl.cases['?'] || []);
25118
    ctrl.cases['?'].push({ transclude: $transclude, element: element });
25119
   }
25120
});
25121
 
25122
/**
25123
 * @ngdoc directive
25124
 * @name ngTransclude
25125
 * @restrict EAC
25126
 *
25127
 * @description
25128
 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
25129
 *
25130
 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
25131
 *
25132
 * @element ANY
25133
 *
25134
 * @example
25135
   <example module="transcludeExample">
25136
     <file name="index.html">
25137
       <script>
25138
         angular.module('transcludeExample', [])
25139
          .directive('pane', function(){
25140
             return {
25141
               restrict: 'E',
25142
               transclude: true,
25143
               scope: { title:'@' },
25144
               template: '<div style="border: 1px solid black;">' +
25145
                           '<div style="background-color: gray">{{title}}</div>' +
25146
                           '<ng-transclude></ng-transclude>' +
25147
                         '</div>'
25148
             };
25149
         })
25150
         .controller('ExampleController', ['$scope', function($scope) {
25151
           $scope.title = 'Lorem Ipsum';
25152
           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
25153
         }]);
25154
       </script>
25155
       <div ng-controller="ExampleController">
25156
         <input ng-model="title"> <br/>
25157
         <textarea ng-model="text"></textarea> <br/>
25158
         <pane title="{{title}}">{{text}}</pane>
25159
       </div>
25160
     </file>
25161
     <file name="protractor.js" type="protractor">
25162
        it('should have transcluded', function() {
25163
          var titleElement = element(by.model('title'));
25164
          titleElement.clear();
25165
          titleElement.sendKeys('TITLE');
25166
          var textElement = element(by.model('text'));
25167
          textElement.clear();
25168
          textElement.sendKeys('TEXT');
25169
          expect(element(by.binding('title')).getText()).toEqual('TITLE');
25170
          expect(element(by.binding('text')).getText()).toEqual('TEXT');
25171
        });
25172
     </file>
25173
   </example>
25174
 *
25175
 */
25176
var ngTranscludeDirective = ngDirective({
25177
  restrict: 'EAC',
25178
  link: function($scope, $element, $attrs, controller, $transclude) {
25179
    if (!$transclude) {
25180
      throw minErr('ngTransclude')('orphan',
25181
       'Illegal use of ngTransclude directive in the template! ' +
25182
       'No parent directive that requires a transclusion found. ' +
25183
       'Element: {0}',
25184
       startingTag($element));
25185
    }
25186
 
25187
    $transclude(function(clone) {
25188
      $element.empty();
25189
      $element.append(clone);
25190
    });
25191
  }
25192
});
25193
 
25194
/**
25195
 * @ngdoc directive
25196
 * @name script
25197
 * @restrict E
25198
 *
25199
 * @description
25200
 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
25201
 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
25202
 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
25203
 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
25204
 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
25205
 *
25206
 * @param {string} type Must be set to `'text/ng-template'`.
25207
 * @param {string} id Cache name of the template.
25208
 *
25209
 * @example
25210
  <example>
25211
    <file name="index.html">
25212
      <script type="text/ng-template" id="/tpl.html">
25213
        Content of the template.
25214
      </script>
25215
 
25216
      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
25217
      <div id="tpl-content" ng-include src="currentTpl"></div>
25218
    </file>
25219
    <file name="protractor.js" type="protractor">
25220
      it('should load template defined inside script tag', function() {
25221
        element(by.css('#tpl-link')).click();
25222
        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
25223
      });
25224
    </file>
25225
  </example>
25226
 */
25227
var scriptDirective = ['$templateCache', function($templateCache) {
25228
  return {
25229
    restrict: 'E',
25230
    terminal: true,
25231
    compile: function(element, attr) {
25232
      if (attr.type == 'text/ng-template') {
25233
        var templateUrl = attr.id,
25234
            text = element[0].text;
25235
 
25236
        $templateCache.put(templateUrl, text);
25237
      }
25238
    }
25239
  };
25240
}];
25241
 
25242
var ngOptionsMinErr = minErr('ngOptions');
25243
/**
25244
 * @ngdoc directive
25245
 * @name select
25246
 * @restrict E
25247
 *
25248
 * @description
25249
 * HTML `SELECT` element with angular data-binding.
25250
 *
25251
 * # `ngOptions`
25252
 *
25253
 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25254
 * elements for the `<select>` element using the array or object obtained by evaluating the
25255
 * `ngOptions` comprehension expression.
25256
 *
25257
 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25258
 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
25259
 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25260
 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
25261
 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
25262
 *  to a non-string value. This is because an option element can only be bound to string values at
25263
 * present.
25264
 *
25265
 * When an item in the `<select>` menu is selected, the array element or object property
25266
 * represented by the selected option will be bound to the model identified by the `ngModel`
25267
 * directive.
25268
 *
25269
 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25270
 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25271
 * option. See example below for demonstration.
25272
 *
25273
 * <div class="alert alert-warning">
25274
 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
25275
 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
25276
 * </div>
25277
 *
25278
 * ## `select` **`as`**
25279
 *
25280
 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
25281
 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25282
 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
25283
 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25284
 *
25285
 *
25286
 * ### `select` **`as`** and **`track by`**
25287
 *
25288
 * <div class="alert alert-warning">
25289
 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
25290
 * </div>
25291
 *
25292
 * Consider the following example:
25293
 *
25294
 * ```html
25295
 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
25296
 * ```
25297
 *
25298
 * ```js
25299
 * $scope.values = [{
25300
 *   id: 1,
25301
 *   label: 'aLabel',
25302
 *   subItem: { name: 'aSubItem' }
25303
 * }, {
25304
 *   id: 2,
25305
 *   label: 'bLabel',
25306
 *   subItem: { name: 'bSubItem' }
25307
 * }];
25308
 *
25309
 * $scope.selected = { name: 'aSubItem' };
25310
 * ```
25311
 *
25312
 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
25313
 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
25314
 * following:
25315
 *
25316
 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
25317
 * 2. Apply **`track by`** to the already selected value in `ngModel`.
25318
 *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
25319
 *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
25320
 *    a wrong object, the selected element can't be found, `<select>` is always reset to the "not
25321
 *    selected" option.
25322
 *
25323
 *
25324
 * @param {string} ngModel Assignable angular expression to data-bind to.
25325
 * @param {string=} name Property name of the form under which the control is published.
25326
 * @param {string=} required The control is considered valid only if value is entered.
25327
 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25328
 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25329
 *    `required` when you want to data-bind to the `required` attribute.
25330
 * @param {comprehension_expression=} ngOptions in one of the following forms:
25331
 *
25332
 *   * for array data sources:
25333
 *     * `label` **`for`** `value` **`in`** `array`
25334
 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25335
 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25336
 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25337
 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
25338
 *        (for including a filter with `track by`)
25339
 *   * for object data sources:
25340
 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25341
 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25342
 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25343
 *     * `select` **`as`** `label` **`group by`** `group`
25344
 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25345
 *
25346
 * Where:
25347
 *
25348
 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25349
 *   * `value`: local variable which will refer to each item in the `array` or each property value
25350
 *      of `object` during iteration.
25351
 *   * `key`: local variable which will refer to a property name in `object` during iteration.
25352
 *   * `label`: The result of this expression will be the label for `<option>` element. The
25353
 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25354
 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
25355
 *      element. If not specified, `select` expression will default to `value`.
25356
 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
25357
 *      DOM element.
25358
 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25359
 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
25360
 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25361
 *      even when the options are recreated (e.g. reloaded from the server).
25362
 *
25363
 * @example
25364
    <example module="selectExample">
25365
      <file name="index.html">
25366
        <script>
25367
        angular.module('selectExample', [])
25368
          .controller('ExampleController', ['$scope', function($scope) {
25369
            $scope.colors = [
25370
              {name:'black', shade:'dark'},
25371
              {name:'white', shade:'light'},
25372
              {name:'red', shade:'dark'},
25373
              {name:'blue', shade:'dark'},
25374
              {name:'yellow', shade:'light'}
25375
            ];
25376
            $scope.myColor = $scope.colors[2]; // red
25377
          }]);
25378
        </script>
25379
        <div ng-controller="ExampleController">
25380
          <ul>
25381
            <li ng-repeat="color in colors">
25382
              Name: <input ng-model="color.name">
25383
              [<a href ng-click="colors.splice($index, 1)">X</a>]
25384
            </li>
25385
            <li>
25386
              [<a href ng-click="colors.push({})">add</a>]
25387
            </li>
25388
          </ul>
25389
          <hr/>
25390
          Color (null not allowed):
25391
          <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
25392
 
25393
          Color (null allowed):
25394
          <span  class="nullable">
25395
            <select ng-model="myColor" ng-options="color.name for color in colors">
25396
              <option value="">-- choose color --</option>
25397
            </select>
25398
          </span><br/>
25399
 
25400
          Color grouped by shade:
25401
          <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25402
          </select><br/>
25403
 
25404
 
25405
          Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
25406
          <hr/>
25407
          Currently selected: {{ {selected_color:myColor} }}
25408
          <div style="border:solid 1px black; height:20px"
25409
               ng-style="{'background-color':myColor.name}">
25410
          </div>
25411
        </div>
25412
      </file>
25413
      <file name="protractor.js" type="protractor">
25414
         it('should check ng-options', function() {
25415
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25416
           element.all(by.model('myColor')).first().click();
25417
           element.all(by.css('select[ng-model="myColor"] option')).first().click();
25418
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25419
           element(by.css('.nullable select[ng-model="myColor"]')).click();
25420
           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25421
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25422
         });
25423
      </file>
25424
    </example>
25425
 */
25426
 
25427
var ngOptionsDirective = valueFn({
25428
  restrict: 'A',
25429
  terminal: true
25430
});
25431
 
25432
// jshint maxlen: false
25433
var selectDirective = ['$compile', '$parse', function($compile,   $parse) {
25434
                         //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
25435
  var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
25436
      nullModelCtrl = {$setViewValue: noop};
25437
// jshint maxlen: 100
25438
 
25439
  return {
25440
    restrict: 'E',
25441
    require: ['select', '?ngModel'],
25442
    controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
25443
      var self = this,
25444
          optionsMap = {},
25445
          ngModelCtrl = nullModelCtrl,
25446
          nullOption,
25447
          unknownOption;
25448
 
25449
 
25450
      self.databound = $attrs.ngModel;
25451
 
25452
 
25453
      self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
25454
        ngModelCtrl = ngModelCtrl_;
25455
        nullOption = nullOption_;
25456
        unknownOption = unknownOption_;
25457
      };
25458
 
25459
 
25460
      self.addOption = function(value, element) {
25461
        assertNotHasOwnProperty(value, '"option value"');
25462
        optionsMap[value] = true;
25463
 
25464
        if (ngModelCtrl.$viewValue == value) {
25465
          $element.val(value);
25466
          if (unknownOption.parent()) unknownOption.remove();
25467
        }
25468
        // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
25469
        // Adding an <option selected="selected"> element to a <select required="required"> should
25470
        // automatically select the new element
25471
        if (element && element[0].hasAttribute('selected')) {
25472
          element[0].selected = true;
25473
        }
25474
      };
25475
 
25476
 
25477
      self.removeOption = function(value) {
25478
        if (this.hasOption(value)) {
25479
          delete optionsMap[value];
25480
          if (ngModelCtrl.$viewValue === value) {
25481
            this.renderUnknownOption(value);
25482
          }
25483
        }
25484
      };
25485
 
25486
 
25487
      self.renderUnknownOption = function(val) {
25488
        var unknownVal = '? ' + hashKey(val) + ' ?';
25489
        unknownOption.val(unknownVal);
25490
        $element.prepend(unknownOption);
25491
        $element.val(unknownVal);
25492
        unknownOption.prop('selected', true); // needed for IE
25493
      };
25494
 
25495
 
25496
      self.hasOption = function(value) {
25497
        return optionsMap.hasOwnProperty(value);
25498
      };
25499
 
25500
      $scope.$on('$destroy', function() {
25501
        // disable unknown option so that we don't do work when the whole select is being destroyed
25502
        self.renderUnknownOption = noop;
25503
      });
25504
    }],
25505
 
25506
    link: function(scope, element, attr, ctrls) {
25507
      // if ngModel is not defined, we don't need to do anything
25508
      if (!ctrls[1]) return;
25509
 
25510
      var selectCtrl = ctrls[0],
25511
          ngModelCtrl = ctrls[1],
25512
          multiple = attr.multiple,
25513
          optionsExp = attr.ngOptions,
25514
          nullOption = false, // if false, user will not be able to select it (used by ngOptions)
25515
          emptyOption,
25516
          renderScheduled = false,
25517
          // we can't just jqLite('<option>') since jqLite is not smart enough
25518
          // to create it in <select> and IE barfs otherwise.
25519
          optionTemplate = jqLite(document.createElement('option')),
25520
          optGroupTemplate =jqLite(document.createElement('optgroup')),
25521
          unknownOption = optionTemplate.clone();
25522
 
25523
      // find "null" option
25524
      for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
25525
        if (children[i].value === '') {
25526
          emptyOption = nullOption = children.eq(i);
25527
          break;
25528
        }
25529
      }
25530
 
25531
      selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
25532
 
25533
      // required validator
25534
      if (multiple) {
25535
        ngModelCtrl.$isEmpty = function(value) {
25536
          return !value || value.length === 0;
25537
        };
25538
      }
25539
 
25540
      if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
25541
      else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
25542
      else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
25543
 
25544
 
25545
      ////////////////////////////
25546
 
25547
 
25548
 
25549
      function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
25550
        ngModelCtrl.$render = function() {
25551
          var viewValue = ngModelCtrl.$viewValue;
25552
 
25553
          if (selectCtrl.hasOption(viewValue)) {
25554
            if (unknownOption.parent()) unknownOption.remove();
25555
            selectElement.val(viewValue);
25556
            if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
25557
          } else {
25558
            if (isUndefined(viewValue) && emptyOption) {
25559
              selectElement.val('');
25560
            } else {
25561
              selectCtrl.renderUnknownOption(viewValue);
25562
            }
25563
          }
25564
        };
25565
 
25566
        selectElement.on('change', function() {
25567
          scope.$apply(function() {
25568
            if (unknownOption.parent()) unknownOption.remove();
25569
            ngModelCtrl.$setViewValue(selectElement.val());
25570
          });
25571
        });
25572
      }
25573
 
25574
      function setupAsMultiple(scope, selectElement, ctrl) {
25575
        var lastView;
25576
        ctrl.$render = function() {
25577
          var items = new HashMap(ctrl.$viewValue);
25578
          forEach(selectElement.find('option'), function(option) {
25579
            option.selected = isDefined(items.get(option.value));
25580
          });
25581
        };
25582
 
25583
        // we have to do it on each watch since ngModel watches reference, but
25584
        // we need to work of an array, so we need to see if anything was inserted/removed
25585
        scope.$watch(function selectMultipleWatch() {
25586
          if (!equals(lastView, ctrl.$viewValue)) {
25587
            lastView = shallowCopy(ctrl.$viewValue);
25588
            ctrl.$render();
25589
          }
25590
        });
25591
 
25592
        selectElement.on('change', function() {
25593
          scope.$apply(function() {
25594
            var array = [];
25595
            forEach(selectElement.find('option'), function(option) {
25596
              if (option.selected) {
25597
                array.push(option.value);
25598
              }
25599
            });
25600
            ctrl.$setViewValue(array);
25601
          });
25602
        });
25603
      }
25604
 
25605
      function setupAsOptions(scope, selectElement, ctrl) {
25606
        var match;
25607
 
25608
        if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25609
          throw ngOptionsMinErr('iexp',
25610
            "Expected expression in form of " +
25611
            "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25612
            " but got '{0}'. Element: {1}",
25613
            optionsExp, startingTag(selectElement));
25614
        }
25615
 
25616
        var displayFn = $parse(match[2] || match[1]),
25617
            valueName = match[4] || match[6],
25618
            selectAs = / as /.test(match[0]) && match[1],
25619
            selectAsFn = selectAs ? $parse(selectAs) : null,
25620
            keyName = match[5],
25621
            groupByFn = $parse(match[3] || ''),
25622
            valueFn = $parse(match[2] ? match[1] : valueName),
25623
            valuesFn = $parse(match[7]),
25624
            track = match[8],
25625
            trackFn = track ? $parse(match[8]) : null,
25626
            trackKeysCache = {},
25627
            // This is an array of array of existing option groups in DOM.
25628
            // We try to reuse these if possible
25629
            // - optionGroupsCache[0] is the options with no option group
25630
            // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
25631
            optionGroupsCache = [[{element: selectElement, label:''}]],
25632
            //re-usable object to represent option's locals
25633
            locals = {};
25634
 
25635
        if (nullOption) {
25636
          // compile the element since there might be bindings in it
25637
          $compile(nullOption)(scope);
25638
 
25639
          // remove the class, which is added automatically because we recompile the element and it
25640
          // becomes the compilation root
25641
          nullOption.removeClass('ng-scope');
25642
 
25643
          // we need to remove it before calling selectElement.empty() because otherwise IE will
25644
          // remove the label from the element. wtf?
25645
          nullOption.remove();
25646
        }
25647
 
25648
        // clear contents, we'll add what's needed based on the model
25649
        selectElement.empty();
25650
 
25651
        selectElement.on('change', selectionChanged);
25652
 
25653
        ctrl.$render = render;
25654
 
25655
        scope.$watchCollection(valuesFn, scheduleRendering);
25656
        scope.$watchCollection(getLabels, scheduleRendering);
25657
 
25658
        if (multiple) {
25659
          scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
25660
        }
25661
 
25662
        // ------------------------------------------------------------------ //
25663
 
25664
        function callExpression(exprFn, key, value) {
25665
          locals[valueName] = value;
25666
          if (keyName) locals[keyName] = key;
25667
          return exprFn(scope, locals);
25668
        }
25669
 
25670
        function selectionChanged() {
25671
          scope.$apply(function() {
25672
            var collection = valuesFn(scope) || [];
25673
            var viewValue;
25674
            if (multiple) {
25675
              viewValue = [];
25676
              forEach(selectElement.val(), function(selectedKey) {
25677
                  selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;
25678
                viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
25679
              });
25680
            } else {
25681
              var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();
25682
              viewValue = getViewValue(selectedKey, collection[selectedKey]);
25683
            }
25684
            ctrl.$setViewValue(viewValue);
25685
            render();
25686
          });
25687
        }
25688
 
25689
        function getViewValue(key, value) {
25690
          if (key === '?') {
25691
            return undefined;
25692
          } else if (key === '') {
25693
            return null;
25694
          } else {
25695
            var viewValueFn = selectAsFn ? selectAsFn : valueFn;
25696
            return callExpression(viewValueFn, key, value);
25697
          }
25698
        }
25699
 
25700
        function getLabels() {
25701
          var values = valuesFn(scope);
25702
          var toDisplay;
25703
          if (values && isArray(values)) {
25704
            toDisplay = new Array(values.length);
25705
            for (var i = 0, ii = values.length; i < ii; i++) {
25706
              toDisplay[i] = callExpression(displayFn, i, values[i]);
25707
            }
25708
            return toDisplay;
25709
          } else if (values) {
25710
            // TODO: Add a test for this case
25711
            toDisplay = {};
25712
            for (var prop in values) {
25713
              if (values.hasOwnProperty(prop)) {
25714
                toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
25715
              }
25716
            }
25717
          }
25718
          return toDisplay;
25719
        }
25720
 
25721
        function createIsSelectedFn(viewValue) {
25722
          var selectedSet;
25723
          if (multiple) {
25724
            if (trackFn && isArray(viewValue)) {
25725
 
25726
              selectedSet = new HashMap([]);
25727
              for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
25728
                // tracking by key
25729
                selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
25730
              }
25731
            } else {
25732
              selectedSet = new HashMap(viewValue);
25733
            }
25734
          } else if (trackFn) {
25735
            viewValue = callExpression(trackFn, null, viewValue);
25736
          }
25737
 
25738
          return function isSelected(key, value) {
25739
            var compareValueFn;
25740
            if (trackFn) {
25741
              compareValueFn = trackFn;
25742
            } else if (selectAsFn) {
25743
              compareValueFn = selectAsFn;
25744
            } else {
25745
              compareValueFn = valueFn;
25746
            }
25747
 
25748
            if (multiple) {
25749
              return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
25750
            } else {
25751
              return viewValue === callExpression(compareValueFn, key, value);
25752
            }
25753
          };
25754
        }
25755
 
25756
        function scheduleRendering() {
25757
          if (!renderScheduled) {
25758
            scope.$$postDigest(render);
25759
            renderScheduled = true;
25760
          }
25761
        }
25762
 
25763
        /**
25764
         * A new labelMap is created with each render.
25765
         * This function is called for each existing option with added=false,
25766
         * and each new option with added=true.
25767
         * - Labels that are passed to this method twice,
25768
         * (once with added=true and once with added=false) will end up with a value of 0, and
25769
         * will cause no change to happen to the corresponding option.
25770
         * - Labels that are passed to this method only once with added=false will end up with a
25771
         * value of -1 and will eventually be passed to selectCtrl.removeOption()
25772
         * - Labels that are passed to this method only once with added=true will end up with a
25773
         * value of 1 and will eventually be passed to selectCtrl.addOption()
25774
        */
25775
        function updateLabelMap(labelMap, label, added) {
25776
          labelMap[label] = labelMap[label] || 0;
25777
          labelMap[label] += (added ? 1 : -1);
25778
        }
25779
 
25780
        function render() {
25781
          renderScheduled = false;
25782
 
25783
          // Temporary location for the option groups before we render them
25784
          var optionGroups = {'':[]},
25785
              optionGroupNames = [''],
25786
              optionGroupName,
25787
              optionGroup,
25788
              option,
25789
              existingParent, existingOptions, existingOption,
25790
              viewValue = ctrl.$viewValue,
25791
              values = valuesFn(scope) || [],
25792
              keys = keyName ? sortedKeys(values) : values,
25793
              key,
25794
              value,
25795
              groupLength, length,
25796
              groupIndex, index,
25797
              labelMap = {},
25798
              selected,
25799
              isSelected = createIsSelectedFn(viewValue),
25800
              anySelected = false,
25801
              lastElement,
25802
              element,
25803
              label,
25804
              optionId;
25805
 
25806
          trackKeysCache = {};
25807
 
25808
          // We now build up the list of options we need (we merge later)
25809
          for (index = 0; length = keys.length, index < length; index++) {
25810
            key = index;
25811
            if (keyName) {
25812
              key = keys[index];
25813
              if (key.charAt(0) === '$') continue;
25814
            }
25815
            value = values[key];
25816
 
25817
            optionGroupName = callExpression(groupByFn, key, value) || '';
25818
            if (!(optionGroup = optionGroups[optionGroupName])) {
25819
              optionGroup = optionGroups[optionGroupName] = [];
25820
              optionGroupNames.push(optionGroupName);
25821
            }
25822
 
25823
            selected = isSelected(key, value);
25824
            anySelected = anySelected || selected;
25825
 
25826
            label = callExpression(displayFn, key, value); // what will be seen by the user
25827
 
25828
            // doing displayFn(scope, locals) || '' overwrites zero values
25829
            label = isDefined(label) ? label : '';
25830
            optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);
25831
            if (trackFn) {
25832
              trackKeysCache[optionId] = key;
25833
            }
25834
 
25835
            optionGroup.push({
25836
              // either the index into array or key from object
25837
              id: optionId,
25838
              label: label,
25839
              selected: selected                   // determine if we should be selected
25840
            });
25841
          }
25842
          if (!multiple) {
25843
            if (nullOption || viewValue === null) {
25844
              // insert null option if we have a placeholder, or the model is null
25845
              optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
25846
            } else if (!anySelected) {
25847
              // option could not be found, we have to insert the undefined item
25848
              optionGroups[''].unshift({id:'?', label:'', selected:true});
25849
            }
25850
          }
25851
 
25852
          // Now we need to update the list of DOM nodes to match the optionGroups we computed above
25853
          for (groupIndex = 0, groupLength = optionGroupNames.length;
25854
               groupIndex < groupLength;
25855
               groupIndex++) {
25856
            // current option group name or '' if no group
25857
            optionGroupName = optionGroupNames[groupIndex];
25858
 
25859
            // list of options for that group. (first item has the parent)
25860
            optionGroup = optionGroups[optionGroupName];
25861
 
25862
            if (optionGroupsCache.length <= groupIndex) {
25863
              // we need to grow the optionGroups
25864
              existingParent = {
25865
                element: optGroupTemplate.clone().attr('label', optionGroupName),
25866
                label: optionGroup.label
25867
              };
25868
              existingOptions = [existingParent];
25869
              optionGroupsCache.push(existingOptions);
25870
              selectElement.append(existingParent.element);
25871
            } else {
25872
              existingOptions = optionGroupsCache[groupIndex];
25873
              existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element
25874
 
25875
              // update the OPTGROUP label if not the same.
25876
              if (existingParent.label != optionGroupName) {
25877
                existingParent.element.attr('label', existingParent.label = optionGroupName);
25878
              }
25879
            }
25880
 
25881
            lastElement = null;  // start at the beginning
25882
            for (index = 0, length = optionGroup.length; index < length; index++) {
25883
              option = optionGroup[index];
25884
              if ((existingOption = existingOptions[index + 1])) {
25885
                // reuse elements
25886
                lastElement = existingOption.element;
25887
                if (existingOption.label !== option.label) {
25888
                  updateLabelMap(labelMap, existingOption.label, false);
25889
                  updateLabelMap(labelMap, option.label, true);
25890
                  lastElement.text(existingOption.label = option.label);
25891
                  lastElement.prop('label', existingOption.label);
25892
                }
25893
                if (existingOption.id !== option.id) {
25894
                  lastElement.val(existingOption.id = option.id);
25895
                }
25896
                // lastElement.prop('selected') provided by jQuery has side-effects
25897
                if (lastElement[0].selected !== option.selected) {
25898
                  lastElement.prop('selected', (existingOption.selected = option.selected));
25899
                  if (msie) {
25900
                    // See #7692
25901
                    // The selected item wouldn't visually update on IE without this.
25902
                    // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
25903
                    lastElement.prop('selected', existingOption.selected);
25904
                  }
25905
                }
25906
              } else {
25907
                // grow elements
25908
 
25909
                // if it's a null option
25910
                if (option.id === '' && nullOption) {
25911
                  // put back the pre-compiled element
25912
                  element = nullOption;
25913
                } else {
25914
                  // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
25915
                  // in this version of jQuery on some browser the .text() returns a string
25916
                  // rather then the element.
25917
                  (element = optionTemplate.clone())
25918
                      .val(option.id)
25919
                      .prop('selected', option.selected)
25920
                      .attr('selected', option.selected)
25921
                      .prop('label', option.label)
25922
                      .text(option.label);
25923
                }
25924
 
25925
                existingOptions.push(existingOption = {
25926
                    element: element,
25927
                    label: option.label,
25928
                    id: option.id,
25929
                    selected: option.selected
25930
                });
25931
                updateLabelMap(labelMap, option.label, true);
25932
                if (lastElement) {
25933
                  lastElement.after(element);
25934
                } else {
25935
                  existingParent.element.append(element);
25936
                }
25937
                lastElement = element;
25938
              }
25939
            }
25940
            // remove any excessive OPTIONs in a group
25941
            index++; // increment since the existingOptions[0] is parent element not OPTION
25942
            while (existingOptions.length > index) {
25943
              option = existingOptions.pop();
25944
              updateLabelMap(labelMap, option.label, false);
25945
              option.element.remove();
25946
            }
25947
          }
25948
          // remove any excessive OPTGROUPs from select
25949
          while (optionGroupsCache.length > groupIndex) {
25950
            // remove all the labels in the option group
25951
            optionGroup = optionGroupsCache.pop();
25952
            for (index = 1; index < optionGroup.length; ++index) {
25953
              updateLabelMap(labelMap, optionGroup[index].label, false);
25954
            }
25955
            optionGroup[0].element.remove();
25956
          }
25957
          forEach(labelMap, function(count, label) {
25958
            if (count > 0) {
25959
              selectCtrl.addOption(label);
25960
            } else if (count < 0) {
25961
              selectCtrl.removeOption(label);
25962
            }
25963
          });
25964
        }
25965
      }
25966
    }
25967
  };
25968
}];
25969
 
25970
var optionDirective = ['$interpolate', function($interpolate) {
25971
  var nullSelectCtrl = {
25972
    addOption: noop,
25973
    removeOption: noop
25974
  };
25975
 
25976
  return {
25977
    restrict: 'E',
25978
    priority: 100,
25979
    compile: function(element, attr) {
25980
      if (isUndefined(attr.value)) {
25981
        var interpolateFn = $interpolate(element.text(), true);
25982
        if (!interpolateFn) {
25983
          attr.$set('value', element.text());
25984
        }
25985
      }
25986
 
25987
      return function(scope, element, attr) {
25988
        var selectCtrlName = '$selectController',
25989
            parent = element.parent(),
25990
            selectCtrl = parent.data(selectCtrlName) ||
25991
              parent.parent().data(selectCtrlName); // in case we are in optgroup
25992
 
25993
        if (!selectCtrl || !selectCtrl.databound) {
25994
          selectCtrl = nullSelectCtrl;
25995
        }
25996
 
25997
        if (interpolateFn) {
25998
          scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
25999
            attr.$set('value', newVal);
26000
            if (oldVal !== newVal) {
26001
              selectCtrl.removeOption(oldVal);
26002
            }
26003
            selectCtrl.addOption(newVal, element);
26004
          });
26005
        } else {
26006
          selectCtrl.addOption(attr.value, element);
26007
        }
26008
 
26009
        element.on('$destroy', function() {
26010
          selectCtrl.removeOption(attr.value);
26011
        });
26012
      };
26013
    }
26014
  };
26015
}];
26016
 
26017
var styleDirective = valueFn({
26018
  restrict: 'E',
26019
  terminal: false
26020
});
26021
 
26022
var requiredDirective = function() {
26023
  return {
26024
    restrict: 'A',
26025
    require: '?ngModel',
26026
    link: function(scope, elm, attr, ctrl) {
26027
      if (!ctrl) return;
26028
      attr.required = true; // force truthy in case we are on non input element
26029
 
26030
      ctrl.$validators.required = function(modelValue, viewValue) {
26031
        return !attr.required || !ctrl.$isEmpty(viewValue);
26032
      };
26033
 
26034
      attr.$observe('required', function() {
26035
        ctrl.$validate();
26036
      });
26037
    }
26038
  };
26039
};
26040
 
26041
 
26042
var patternDirective = function() {
26043
  return {
26044
    restrict: 'A',
26045
    require: '?ngModel',
26046
    link: function(scope, elm, attr, ctrl) {
26047
      if (!ctrl) return;
26048
 
26049
      var regexp, patternExp = attr.ngPattern || attr.pattern;
26050
      attr.$observe('pattern', function(regex) {
26051
        if (isString(regex) && regex.length > 0) {
26052
          regex = new RegExp('^' + regex + '$');
26053
        }
26054
 
26055
        if (regex && !regex.test) {
26056
          throw minErr('ngPattern')('noregexp',
26057
            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
26058
            regex, startingTag(elm));
26059
        }
26060
 
26061
        regexp = regex || undefined;
26062
        ctrl.$validate();
26063
      });
26064
 
26065
      ctrl.$validators.pattern = function(value) {
26066
        return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
26067
      };
26068
    }
26069
  };
26070
};
26071
 
26072
 
26073
var maxlengthDirective = function() {
26074
  return {
26075
    restrict: 'A',
26076
    require: '?ngModel',
26077
    link: function(scope, elm, attr, ctrl) {
26078
      if (!ctrl) return;
26079
 
26080
      var maxlength = -1;
26081
      attr.$observe('maxlength', function(value) {
26082
        var intVal = int(value);
26083
        maxlength = isNaN(intVal) ? -1 : intVal;
26084
        ctrl.$validate();
26085
      });
26086
      ctrl.$validators.maxlength = function(modelValue, viewValue) {
26087
        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
26088
      };
26089
    }
26090
  };
26091
};
26092
 
26093
var minlengthDirective = function() {
26094
  return {
26095
    restrict: 'A',
26096
    require: '?ngModel',
26097
    link: function(scope, elm, attr, ctrl) {
26098
      if (!ctrl) return;
26099
 
26100
      var minlength = 0;
26101
      attr.$observe('minlength', function(value) {
26102
        minlength = int(value) || 0;
26103
        ctrl.$validate();
26104
      });
26105
      ctrl.$validators.minlength = function(modelValue, viewValue) {
26106
        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
26107
      };
26108
    }
26109
  };
26110
};
26111
 
26112
  if (window.angular.bootstrap) {
26113
    //AngularJS is already loaded, so we can return here...
26114
    console.log('WARNING: Tried to load angular more than once.');
26115
    return;
26116
  }
26117
 
26118
  //try to bind to jquery now so that one can write jqLite(document).ready()
26119
  //but we will rebind on bootstrap again.
26120
  bindJQuery();
26121
 
26122
  publishExternalAPI(angular);
26123
 
26124
  jqLite(document).ready(function() {
26125
    angularInit(document, bootstrap);
26126
  });
26127
 
26128
})(window, document);
26129
 
26130
!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');