Showing the changes to Source/Libraries/GSF.Web/Shared/Scripts/angular.js (1 of 36 files). Show all
Unified · Split
| @@ -0,0 +1,31768 @@ | |||
| 1 | /** | ||
| 2 | * @license AngularJS v1.5.8 | ||
| 3 | * (c) 2010-2016 Google, Inc. http://angularjs.org | ||
| 4 | * License: MIT | ||
| 5 | */ | ||
| 6 | (function(window) {'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 SKIP_INDEXES = 2; | ||
| 42 | |||
| 43 | var templateArgs = arguments, | ||
| 44 | code = templateArgs[0], | ||
| 45 | message = '[' + (module ? module + ':' : '') + code + '] ', | ||
| 46 | template = templateArgs[1], | ||
| 47 | paramPrefix, i; | ||
| 48 | |||
| 49 | message += template.replace(/\{\d+\}/g, function(match) { | ||
| 50 | var index = +match.slice(1, -1), | ||
| 51 | shiftedIndex = index + SKIP_INDEXES; | ||
| 52 | |||
| 53 | if (shiftedIndex < templateArgs.length) { | ||
| 54 | return toDebugString(templateArgs[shiftedIndex]); | ||
| 55 | } | ||
| 56 | |||
| 57 | return match; | ||
| 58 | }); | ||
| 59 | |||
| 60 | message += '\nhttp://errors.angularjs.org/1.5.8/' + | ||
| 61 | (module ? module + '/' : '') + code; | ||
| 62 | |||
| 63 | for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { | ||
| 64 | message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + | ||
| 65 | encodeURIComponent(toDebugString(templateArgs[i])); | ||
| 66 | } | ||
| 67 | |||
| 68 | return new ErrorConstructor(message); | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | /* We need to tell jshint what variables are being exported */ | ||
| 73 | /* global angular: true, | ||
| 74 | msie: true, | ||
| 75 | jqLite: true, | ||
| 76 | jQuery: true, | ||
| 77 | slice: true, | ||
| 78 | splice: true, | ||
| 79 | push: true, | ||
| 80 | toString: true, | ||
| 81 | ngMinErr: true, | ||
| 82 | angularModule: true, | ||
| 83 | uid: true, | ||
| 84 | REGEX_STRING_REGEXP: true, | ||
| 85 | VALIDITY_STATE_PROPERTY: true, | ||
| 86 | |||
| 87 | lowercase: true, | ||
| 88 | uppercase: true, | ||
| 89 | manualLowercase: true, | ||
| 90 | manualUppercase: true, | ||
| 91 | nodeName_: true, | ||
| 92 | isArrayLike: true, | ||
| 93 | forEach: true, | ||
| 94 | forEachSorted: true, | ||
| 95 | reverseParams: true, | ||
| 96 | nextUid: true, | ||
| 97 | setHashKey: true, | ||
| 98 | extend: true, | ||
| 99 | toInt: true, | ||
| 100 | inherit: true, | ||
| 101 | merge: true, | ||
| 102 | noop: true, | ||
| 103 | identity: true, | ||
| 104 | valueFn: true, | ||
| 105 | isUndefined: true, | ||
| 106 | isDefined: true, | ||
| 107 | isObject: true, | ||
| 108 | isBlankObject: true, | ||
| 109 | isString: true, | ||
| 110 | isNumber: true, | ||
| 111 | isDate: true, | ||
| 112 | isArray: true, | ||
| 113 | isFunction: true, | ||
| 114 | isRegExp: true, | ||
| 115 | isWindow: true, | ||
| 116 | isScope: true, | ||
| 117 | isFile: true, | ||
| 118 | isFormData: true, | ||
| 119 | isBlob: true, | ||
| 120 | isBoolean: true, | ||
| 121 | isPromiseLike: true, | ||
| 122 | trim: true, | ||
| 123 | escapeForRegexp: true, | ||
| 124 | isElement: true, | ||
| 125 | makeMap: true, | ||
| 126 | includes: true, | ||
| 127 | arrayRemove: true, | ||
| 128 | copy: true, | ||
| 129 | equals: true, | ||
| 130 | csp: true, | ||
| 131 | jq: true, | ||
| 132 | concat: true, | ||
| 133 | sliceArgs: true, | ||
| 134 | bind: true, | ||
| 135 | toJsonReplacer: true, | ||
| 136 | toJson: true, | ||
| 137 | fromJson: true, | ||
| 138 | convertTimezoneToLocal: true, | ||
| 139 | timezoneToOffset: true, | ||
| 140 | startingTag: true, | ||
| 141 | tryDecodeURIComponent: true, | ||
| 142 | parseKeyValue: true, | ||
| 143 | toKeyValue: true, | ||
| 144 | encodeUriSegment: true, | ||
| 145 | encodeUriQuery: true, | ||
| 146 | angularInit: true, | ||
| 147 | bootstrap: true, | ||
| 148 | getTestability: true, | ||
| 149 | snake_case: true, | ||
| 150 | bindJQuery: true, | ||
| 151 | assertArg: true, | ||
| 152 | assertArgFn: true, | ||
| 153 | assertNotHasOwnProperty: true, | ||
| 154 | getter: true, | ||
| 155 | getBlockNodes: true, | ||
| 156 | hasOwnProperty: true, | ||
| 157 | createMap: true, | ||
| 158 | |||
| 159 | NODE_TYPE_ELEMENT: true, | ||
| 160 | NODE_TYPE_ATTRIBUTE: true, | ||
| 161 | NODE_TYPE_TEXT: true, | ||
| 162 | NODE_TYPE_COMMENT: true, | ||
| 163 | NODE_TYPE_DOCUMENT: true, | ||
| 164 | NODE_TYPE_DOCUMENT_FRAGMENT: true, | ||
| 165 | */ | ||
| 166 | |||
| 167 | //////////////////////////////////// | ||
| 168 | |||
| 169 | /** | ||
| 170 | * @ngdoc module | ||
| 171 | * @name ng | ||
| 172 | * @module ng | ||
| 173 | * @installation | ||
| 174 | * @description | ||
| 175 | * | ||
| 176 | * # ng (core module) | ||
| 177 | * The ng module is loaded by default when an AngularJS application is started. The module itself | ||
| 178 | * contains the essential components for an AngularJS application to function. The table below | ||
| 179 | * lists a high level breakdown of each of the services/factories, filters, directives and testing | ||
| 180 | * components available within this core module. | ||
| 181 | * | ||
| 182 | * <div doc-module-components="ng"></div> | ||
| 183 | */ | ||
| 184 | |||
| 185 | var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; | ||
| 186 | |||
| 187 | // The name of a form control's ValidityState property. | ||
| 188 | // This is used so that it's possible for internal tests to create mock ValidityStates. | ||
| 189 | var VALIDITY_STATE_PROPERTY = 'validity'; | ||
| 190 | |||
| 191 | var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
| 192 | |||
| 193 | var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; | ||
| 194 | var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; | ||
| 195 | |||
| 196 | |||
| 197 | var manualLowercase = function(s) { | ||
| 198 | /* jshint bitwise: false */ | ||
| 199 | return isString(s) | ||
| 200 | ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) | ||
| 201 | : s; | ||
| 202 | }; | ||
| 203 | var manualUppercase = function(s) { | ||
| 204 | /* jshint bitwise: false */ | ||
| 205 | return isString(s) | ||
| 206 | ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) | ||
| 207 | : s; | ||
| 208 | }; | ||
| 209 | |||
| 210 | |||
| 211 | // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish | ||
| 212 | // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods | ||
| 213 | // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387 | ||
| 214 | if ('i' !== 'I'.toLowerCase()) { | ||
| 215 | lowercase = manualLowercase; | ||
| 216 | uppercase = manualUppercase; | ||
| 217 | } | ||
| 218 | |||
| 219 | |||
| 220 | var | ||
| 221 | msie, // holds major version number for IE, or NaN if UA is not IE. | ||
| 222 | jqLite, // delay binding since jQuery could be loaded after us. | ||
| 223 | jQuery, // delay binding | ||
| 224 | slice = [].slice, | ||
| 225 | splice = [].splice, | ||
| 226 | push = [].push, | ||
| 227 | toString = Object.prototype.toString, | ||
| 228 | getPrototypeOf = Object.getPrototypeOf, | ||
| 229 | ngMinErr = minErr('ng'), | ||
| 230 | |||
| 231 | /** @name angular */ | ||
| 232 | angular = window.angular || (window.angular = {}), | ||
| 233 | angularModule, | ||
| 234 | uid = 0; | ||
| 235 | |||
| 236 | /** | ||
| 237 | * documentMode is an IE-only property | ||
| 238 | * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx | ||
| 239 | */ | ||
| 240 | msie = window.document.documentMode; | ||
| 241 | |||
| 242 | |||
| 243 | /** | ||
| 244 | * @private | ||
| 245 | * @param {*} obj | ||
| 246 | * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, | ||
| 247 | * String ...) | ||
| 248 | */ | ||
| 249 | function isArrayLike(obj) { | ||
| 250 | |||
| 251 | // `null`, `undefined` and `window` are not array-like | ||
| 252 | if (obj == null || isWindow(obj)) return false; | ||
| 253 | |||
| 254 | // arrays, strings and jQuery/jqLite objects are array like | ||
| 255 | // * jqLite is either the jQuery or jqLite constructor function | ||
| 256 | // * we have to check the existence of jqLite first as this method is called | ||
| 257 | // via the forEach method when constructing the jqLite object in the first place | ||
| 258 | if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true; | ||
| 259 | |||
| 260 | // Support: iOS 8.2 (not reproducible in simulator) | ||
| 261 | // "length" in obj used to prevent JIT error (gh-11508) | ||
| 262 | var length = "length" in Object(obj) && obj.length; | ||
| 263 | |||
| 264 | // NodeList objects (with `item` method) and | ||
| 265 | // other objects with suitable length characteristics are array-like | ||
| 266 | return isNumber(length) && | ||
| 267 | (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function'); | ||
| 268 | |||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * @ngdoc function | ||
| 273 | * @name angular.forEach | ||
| 274 | * @module ng | ||
| 275 | * @kind function | ||
| 276 | * | ||
| 277 | * @description | ||
| 278 | * Invokes the `iterator` function once for each item in `obj` collection, which can be either an | ||
| 279 | * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` | ||
| 280 | * is the value of an object property or an array element, `key` is the object property key or | ||
| 281 | * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. | ||
| 282 | * | ||
| 283 | * It is worth noting that `.forEach` does not iterate over inherited properties because it filters | ||
| 284 | * using the `hasOwnProperty` method. | ||
| 285 | * | ||
| 286 | * Unlike ES262's | ||
| 287 | * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), | ||
| 288 | * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just | ||
| 289 | * return the value provided. | ||
| 290 | * | ||
| 291 | ```js | ||
| 292 | var values = {name: 'misko', gender: 'male'}; | ||
| 293 | var log = []; | ||
| 294 | angular.forEach(values, function(value, key) { | ||
| 295 | this.push(key + ': ' + value); | ||
| 296 | }, log); | ||
| 297 | expect(log).toEqual(['name: misko', 'gender: male']); | ||
| 298 | ``` | ||
| 299 | * | ||
| 300 | * @param {Object|Array} obj Object to iterate over. | ||
| 301 | * @param {Function} iterator Iterator function. | ||
| 302 | * @param {Object=} context Object to become context (`this`) for the iterator function. | ||
| 303 | * @returns {Object|Array} Reference to `obj`. | ||
| 304 | */ | ||
| 305 | |||
| 306 | function forEach(obj, iterator, context) { | ||
| 307 | var key, length; | ||
| 308 | if (obj) { | ||
| 309 | if (isFunction(obj)) { | ||
| 310 | for (key in obj) { | ||
| 311 | // Need to check if hasOwnProperty exists, | ||
| 312 | // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function | ||
| 313 | if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { | ||
| 314 | iterator.call(context, obj[key], key, obj); | ||
| 315 | } | ||
| 316 | } | ||
| 317 | } else if (isArray(obj) || isArrayLike(obj)) { | ||
| 318 | var isPrimitive = typeof obj !== 'object'; | ||
| 319 | for (key = 0, length = obj.length; key < length; key++) { | ||
| 320 | if (isPrimitive || key in obj) { | ||
| 321 | iterator.call(context, obj[key], key, obj); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | } else if (obj.forEach && obj.forEach !== forEach) { | ||
| 325 | obj.forEach(iterator, context, obj); | ||
| 326 | } else if (isBlankObject(obj)) { | ||
| 327 | // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty | ||
| 328 | for (key in obj) { | ||
| 329 | iterator.call(context, obj[key], key, obj); | ||
| 330 | } | ||
| 331 | } else if (typeof obj.hasOwnProperty === 'function') { | ||
| 332 | // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed | ||
| 333 | for (key in obj) { | ||
| 334 | if (obj.hasOwnProperty(key)) { | ||
| 335 | iterator.call(context, obj[key], key, obj); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } else { | ||
| 339 | // Slow path for objects which do not have a method `hasOwnProperty` | ||
| 340 | for (key in obj) { | ||
| 341 | if (hasOwnProperty.call(obj, key)) { | ||
| 342 | iterator.call(context, obj[key], key, obj); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | } | ||
| 347 | return obj; | ||
| 348 | } | ||
| 349 | |||
| 350 | function forEachSorted(obj, iterator, context) { | ||
| 351 | var keys = Object.keys(obj).sort(); | ||
| 352 | for (var i = 0; i < keys.length; i++) { | ||
| 353 | iterator.call(context, obj[keys[i]], keys[i]); | ||
| 354 | } | ||
| 355 | return keys; | ||
| 356 | } | ||
| 357 | |||
| 358 | |||
| 359 | /** | ||
| 360 | * when using forEach the params are value, key, but it is often useful to have key, value. | ||
| 361 | * @param {function(string, *)} iteratorFn | ||
| 362 | * @returns {function(*, string)} | ||
| 363 | */ | ||
| 364 | function reverseParams(iteratorFn) { | ||
| 365 | return function(value, key) {iteratorFn(key, value);}; | ||
| 366 | } | ||
| 367 | |||
| 368 | /** | ||
| 369 | * A consistent way of creating unique IDs in angular. | ||
| 370 | * | ||
| 371 | * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before | ||
| 372 | * we hit number precision issues in JavaScript. | ||
| 373 | * | ||
| 374 | * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M | ||
| 375 | * | ||
| 376 | * @returns {number} an unique alpha-numeric string | ||
| 377 | */ | ||
| 378 | function nextUid() { | ||
| 379 | return ++uid; | ||
| 380 | } | ||
| 381 | |||
| 382 | |||
| 383 | /** | ||
| 384 | * Set or clear the hashkey for an object. | ||
| 385 | * @param obj object | ||
| 386 | * @param h the hashkey (!truthy to delete the hashkey) | ||
| 387 | */ | ||
| 388 | function setHashKey(obj, h) { | ||
| 389 | if (h) { | ||
| 390 | obj.$$hashKey = h; | ||
| 391 | } else { | ||
| 392 | delete obj.$$hashKey; | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | |||
| 397 | function baseExtend(dst, objs, deep) { | ||
| 398 | var h = dst.$$hashKey; | ||
| 399 | |||
| 400 | for (var i = 0, ii = objs.length; i < ii; ++i) { | ||
| 401 | var obj = objs[i]; | ||
| 402 | if (!isObject(obj) && !isFunction(obj)) continue; | ||
| 403 | var keys = Object.keys(obj); | ||
| 404 | for (var j = 0, jj = keys.length; j < jj; j++) { | ||
| 405 | var key = keys[j]; | ||
| 406 | var src = obj[key]; | ||
| 407 | |||
| 408 | if (deep && isObject(src)) { | ||
| 409 | if (isDate(src)) { | ||
| 410 | dst[key] = new Date(src.valueOf()); | ||
| 411 | } else if (isRegExp(src)) { | ||
| 412 | dst[key] = new RegExp(src); | ||
| 413 | } else if (src.nodeName) { | ||
| 414 | dst[key] = src.cloneNode(true); | ||
| 415 | } else if (isElement(src)) { | ||
| 416 | dst[key] = src.clone(); | ||
| 417 | } else { | ||
| 418 | if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; | ||
| 419 | baseExtend(dst[key], [src], true); | ||
| 420 | } | ||
| 421 | } else { | ||
| 422 | dst[key] = src; | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | setHashKey(dst, h); | ||
| 428 | return dst; | ||
| 429 | } | ||
| 430 | |||
| 431 | /** | ||
| 432 | * @ngdoc function | ||
| 433 | * @name angular.extend | ||
| 434 | * @module ng | ||
| 435 | * @kind function | ||
| 436 | * | ||
| 437 | * @description | ||
| 438 | * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) | ||
| 439 | * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so | ||
| 440 | * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. | ||
| 441 | * | ||
| 442 | * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use | ||
| 443 | * {@link angular.merge} for this. | ||
| 444 | * | ||
| 445 | * @param {Object} dst Destination object. | ||
| 446 | * @param {...Object} src Source object(s). | ||
| 447 | * @returns {Object} Reference to `dst`. | ||
| 448 | */ | ||
| 449 | function extend(dst) { | ||
| 450 | return baseExtend(dst, slice.call(arguments, 1), false); | ||
| 451 | } | ||
| 452 | |||
| 453 | |||
| 454 | /** | ||
| 455 | * @ngdoc function | ||
| 456 | * @name angular.merge | ||
| 457 | * @module ng | ||
| 458 | * @kind function | ||
| 459 | * | ||
| 460 | * @description | ||
| 461 | * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) | ||
| 462 | * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so | ||
| 463 | * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. | ||
| 464 | * | ||
| 465 | * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source | ||
| 466 | * objects, performing a deep copy. | ||
| 467 | * | ||
| 468 | * @param {Object} dst Destination object. | ||
| 469 | * @param {...Object} src Source object(s). | ||
| 470 | * @returns {Object} Reference to `dst`. | ||
| 471 | */ | ||
| 472 | function merge(dst) { | ||
| 473 | return baseExtend(dst, slice.call(arguments, 1), true); | ||
| 474 | } | ||
| 475 | |||
| 476 | |||
| 477 | |||
| 478 | function toInt(str) { | ||
| 479 | return parseInt(str, 10); | ||
| 480 | } | ||
| 481 | |||
| 482 | |||
| 483 | function inherit(parent, extra) { | ||
| 484 | return extend(Object.create(parent), extra); | ||
| 485 | } | ||
| 486 | |||
| 487 | /** | ||
| 488 | * @ngdoc function | ||
| 489 | * @name angular.noop | ||
| 490 | * @module ng | ||
| 491 | * @kind function | ||
| 492 | * | ||
| 493 | * @description | ||
| 494 | * A function that performs no operations. This function can be useful when writing code in the | ||
| 495 | * functional style. | ||
| 496 | ```js | ||
| 497 | function foo(callback) { | ||
| 498 | var result = calculateResult(); | ||
| 499 | (callback || angular.noop)(result); | ||
| 500 | } | ||
| 501 | ``` | ||
| 502 | */ | ||
| 503 | function noop() {} | ||
| 504 | noop.$inject = []; | ||
| 505 | |||
| 506 | |||
| 507 | /** | ||
| 508 | * @ngdoc function | ||
| 509 | * @name angular.identity | ||
| 510 | * @module ng | ||
| 511 | * @kind function | ||
| 512 | * | ||
| 513 | * @description | ||
| 514 | * A function that returns its first argument. This function is useful when writing code in the | ||
| 515 | * functional style. | ||
| 516 | * | ||
| 517 | ```js | ||
| 518 | function transformer(transformationFn, value) { | ||
| 519 | return (transformationFn || angular.identity)(value); | ||
| 520 | }; | ||
| 521 | |||
| 522 | // E.g. | ||
| 523 | function getResult(fn, input) { | ||
| 524 | return (fn || angular.identity)(input); | ||
| 525 | }; | ||
| 526 | |||
| 527 | getResult(function(n) { return n * 2; }, 21); // returns 42 | ||
| 528 | getResult(null, 21); // returns 21 | ||
| 529 | getResult(undefined, 21); // returns 21 | ||
| 530 | ``` | ||
| 531 | * | ||
| 532 | * @param {*} value to be returned. | ||
| 533 | * @returns {*} the value passed in. | ||
| 534 | */ | ||
| 535 | function identity($) {return $;} | ||
| 536 | identity.$inject = []; | ||
| 537 | |||
| 538 | |||
| 539 | function valueFn(value) {return function valueRef() {return value;};} | ||
| 540 | |||
| 541 | function hasCustomToString(obj) { | ||
| 542 | return isFunction(obj.toString) && obj.toString !== toString; | ||
| 543 | } | ||
| 544 | |||
| 545 | |||
| 546 | /** | ||
| 547 | * @ngdoc function | ||
| 548 | * @name angular.isUndefined | ||
| 549 | * @module ng | ||
| 550 | * @kind function | ||
| 551 | * | ||
| 552 | * @description | ||
| 553 | * Determines if a reference is undefined. | ||
| 554 | * | ||
| 555 | * @param {*} value Reference to check. | ||
| 556 | * @returns {boolean} True if `value` is undefined. | ||
| 557 | */ | ||
| 558 | function isUndefined(value) {return typeof value === 'undefined';} | ||
| 559 | |||
| 560 | |||
| 561 | /** | ||
| 562 | * @ngdoc function | ||
| 563 | * @name angular.isDefined | ||
| 564 | * @module ng | ||
| 565 | * @kind function | ||
| 566 | * | ||
| 567 | * @description | ||
| 568 | * Determines if a reference is defined. | ||
| 569 | * | ||
| 570 | * @param {*} value Reference to check. | ||
| 571 | * @returns {boolean} True if `value` is defined. | ||
| 572 | */ | ||
| 573 | function isDefined(value) {return typeof value !== 'undefined';} | ||
| 574 | |||
| 575 | |||
| 576 | /** | ||
| 577 | * @ngdoc function | ||
| 578 | * @name angular.isObject | ||
| 579 | * @module ng | ||
| 580 | * @kind function | ||
| 581 | * | ||
| 582 | * @description | ||
| 583 | * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not | ||
| 584 | * considered to be objects. Note that JavaScript arrays are objects. | ||
| 585 | * | ||
| 586 | * @param {*} value Reference to check. | ||
| 587 | * @returns {boolean} True if `value` is an `Object` but not `null`. | ||
| 588 | */ | ||
| 589 | function isObject(value) { | ||
| 590 | // http://jsperf.com/isobject4 | ||
| 591 | return value !== null && typeof value === 'object'; | ||
| 592 | } | ||
| 593 | |||
| 594 | |||
| 595 | /** | ||
| 596 | * Determine if a value is an object with a null prototype | ||
| 597 | * | ||
| 598 | * @returns {boolean} True if `value` is an `Object` with a null prototype | ||
| 599 | */ | ||
| 600 | function isBlankObject(value) { | ||
| 601 | return value !== null && typeof value === 'object' && !getPrototypeOf(value); | ||
| 602 | } | ||
| 603 | |||
| 604 | |||
| 605 | /** | ||
| 606 | * @ngdoc function | ||
| 607 | * @name angular.isString | ||
| 608 | * @module ng | ||
| 609 | * @kind function | ||
| 610 | * | ||
| 611 | * @description | ||
| 612 | * Determines if a reference is a `String`. | ||
| 613 | * | ||
| 614 | * @param {*} value Reference to check. | ||
| 615 | * @returns {boolean} True if `value` is a `String`. | ||
| 616 | */ | ||
| 617 | function isString(value) {return typeof value === 'string';} | ||
| 618 | |||
| 619 | |||
| 620 | /** | ||
| 621 | * @ngdoc function | ||
| 622 | * @name angular.isNumber | ||
| 623 | * @module ng | ||
| 624 | * @kind function | ||
| 625 | * | ||
| 626 | * @description | ||
| 627 | * Determines if a reference is a `Number`. | ||
| 628 | * | ||
| 629 | * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. | ||
| 630 | * | ||
| 631 | * If you wish to exclude these then you can use the native | ||
| 632 | * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) | ||
| 633 | * method. | ||
| 634 | * | ||
| 635 | * @param {*} value Reference to check. | ||
| 636 | * @returns {boolean} True if `value` is a `Number`. | ||
| 637 | */ | ||
| 638 | function isNumber(value) {return typeof value === 'number';} | ||
| 639 | |||
| 640 | |||
| 641 | /** | ||
| 642 | * @ngdoc function | ||
| 643 | * @name angular.isDate | ||
| 644 | * @module ng | ||
| 645 | * @kind function | ||
| 646 | * | ||
| 647 | * @description | ||
| 648 | * Determines if a value is a date. | ||
| 649 | * | ||
| 650 | * @param {*} value Reference to check. | ||
| 651 | * @returns {boolean} True if `value` is a `Date`. | ||
| 652 | */ | ||
| 653 | function isDate(value) { | ||
| 654 | return toString.call(value) === '[object Date]'; | ||
| 655 | } | ||
| 656 | |||
| 657 | |||
| 658 | /** | ||
| 659 | * @ngdoc function | ||
| 660 | * @name angular.isArray | ||
| 661 | * @module ng | ||
| 662 | * @kind function | ||
| 663 | * | ||
| 664 | * @description | ||
| 665 | * Determines if a reference is an `Array`. | ||
| 666 | * | ||
| 667 | * @param {*} value Reference to check. | ||
| 668 | * @returns {boolean} True if `value` is an `Array`. | ||
| 669 | */ | ||
| 670 | var isArray = Array.isArray; | ||
| 671 | |||
| 672 | /** | ||
| 673 | * @ngdoc function | ||
| 674 | * @name angular.isFunction | ||
| 675 | * @module ng | ||
| 676 | * @kind function | ||
| 677 | * | ||
| 678 | * @description | ||
| 679 | * Determines if a reference is a `Function`. | ||
| 680 | * | ||
| 681 | * @param {*} value Reference to check. | ||
| 682 | * @returns {boolean} True if `value` is a `Function`. | ||
| 683 | */ | ||
| 684 | function isFunction(value) {return typeof value === 'function';} | ||
| 685 | |||
| 686 | |||
| 687 | /** | ||
| 688 | * Determines if a value is a regular expression object. | ||
| 689 | * | ||
| 690 | * @private | ||
| 691 | * @param {*} value Reference to check. | ||
| 692 | * @returns {boolean} True if `value` is a `RegExp`. | ||
| 693 | */ | ||
| 694 | function isRegExp(value) { | ||
| 695 | return toString.call(value) === '[object RegExp]'; | ||
| 696 | } | ||
| 697 | |||
| 698 | |||
| 699 | /** | ||
| 700 | * Checks if `obj` is a window object. | ||
| 701 | * | ||
| 702 | * @private | ||
| 703 | * @param {*} obj Object to check | ||
| 704 | * @returns {boolean} True if `obj` is a window obj. | ||
| 705 | */ | ||
| 706 | function isWindow(obj) { | ||
| 707 | return obj && obj.window === obj; | ||
| 708 | } | ||
| 709 | |||
| 710 | |||
| 711 | function isScope(obj) { | ||
| 712 | return obj && obj.$evalAsync && obj.$watch; | ||
| 713 | } | ||
| 714 | |||
| 715 | |||
| 716 | function isFile(obj) { | ||
| 717 | return toString.call(obj) === '[object File]'; | ||
| 718 | } | ||
| 719 | |||
| 720 | |||
| 721 | function isFormData(obj) { | ||
| 722 | return toString.call(obj) === '[object FormData]'; | ||
| 723 | } | ||
| 724 | |||
| 725 | |||
| 726 | function isBlob(obj) { | ||
| 727 | return toString.call(obj) === '[object Blob]'; | ||
| 728 | } | ||
| 729 | |||
| 730 | |||
| 731 | function isBoolean(value) { | ||
| 732 | return typeof value === 'boolean'; | ||
| 733 | } | ||
| 734 | |||
| 735 | |||
| 736 | function isPromiseLike(obj) { | ||
| 737 | return obj && isFunction(obj.then); | ||
| 738 | } | ||
| 739 | |||
| 740 | |||
| 741 | var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/; | ||
| 742 | function isTypedArray(value) { | ||
| 743 | return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value)); | ||
| 744 | } | ||
| 745 | |||
| 746 | function isArrayBuffer(obj) { | ||
| 747 | return toString.call(obj) === '[object ArrayBuffer]'; | ||
| 748 | } | ||
| 749 | |||
| 750 | |||
| 751 | var trim = function(value) { | ||
| 752 | return isString(value) ? value.trim() : value; | ||
| 753 | }; | ||
| 754 | |||
| 755 | // Copied from: | ||
| 756 | // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 | ||
| 757 | // Prereq: s is a string. | ||
| 758 | var escapeForRegexp = function(s) { | ||
| 759 | return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). | ||
| 760 | replace(/\x08/g, '\\x08'); | ||
| 761 | }; | ||
| 762 | |||
| 763 | |||
| 764 | /** | ||
| 765 | * @ngdoc function | ||
| 766 | * @name angular.isElement | ||
| 767 | * @module ng | ||
| 768 | * @kind function | ||
| 769 | * | ||
| 770 | * @description | ||
| 771 | * Determines if a reference is a DOM element (or wrapped jQuery element). | ||
| 772 | * | ||
| 773 | * @param {*} value Reference to check. | ||
| 774 | * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). | ||
| 775 | */ | ||
| 776 | function isElement(node) { | ||
| 777 | return !!(node && | ||
| 778 | (node.nodeName // We are a direct element. | ||
| 779 | || (node.prop && node.attr && node.find))); // We have an on and find method part of jQuery API. | ||
| 780 | } | ||
| 781 | |||
| 782 | /** | ||
| 783 | * @param str 'key1,key2,...' | ||
| 784 | * @returns {object} in the form of {key1:true, key2:true, ...} | ||
| 785 | */ | ||
| 786 | function makeMap(str) { | ||
| 787 | var obj = {}, items = str.split(','), i; | ||
| 788 | for (i = 0; i < items.length; i++) { | ||
| 789 | obj[items[i]] = true; | ||
| 790 | } | ||
| 791 | return obj; | ||
| 792 | } | ||
| 793 | |||
| 794 | |||
| 795 | function nodeName_(element) { | ||
| 796 | return lowercase(element.nodeName || (element[0] && element[0].nodeName)); | ||
| 797 | } | ||
| 798 | |||
| 799 | function includes(array, obj) { | ||
| 800 | return Array.prototype.indexOf.call(array, obj) != -1; | ||
| 801 | } | ||
| 802 | |||
| 803 | function arrayRemove(array, value) { | ||
| 804 | var index = array.indexOf(value); | ||
| 805 | if (index >= 0) { | ||
| 806 | array.splice(index, 1); | ||
| 807 | } | ||
| 808 | return index; | ||
| 809 | } | ||
| 810 | |||
| 811 | /** | ||
| 812 | * @ngdoc function | ||
| 813 | * @name angular.copy | ||
| 814 | * @module ng | ||
| 815 | * @kind function | ||
| 816 | * | ||
| 817 | * @description | ||
| 818 | * Creates a deep copy of `source`, which should be an object or an array. | ||
| 819 | * | ||
| 820 | * * If no destination is supplied, a copy of the object or array is created. | ||
| 821 | * * If a destination is provided, all of its elements (for arrays) or properties (for objects) | ||
| 822 | * are deleted and then all elements/properties from the source are copied to it. | ||
| 823 | * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. | ||
| 824 | * * If `source` is identical to `destination` an exception will be thrown. | ||
| 825 | * | ||
| 826 | * <br /> | ||
| 827 | * <div class="alert alert-warning"> | ||
| 828 | * Only enumerable properties are taken into account. Non-enumerable properties (both on `source` | ||
| 829 | * and on `destination`) will be ignored. | ||
| 830 | * </div> | ||
| 831 | * | ||
| 832 | * @param {*} source The source that will be used to make a copy. | ||
| 833 | * Can be any type, including primitives, `null`, and `undefined`. | ||
| 834 | * @param {(Object|Array)=} destination Destination into which the source is copied. If | ||
| 835 | * provided, must be of the same type as `source`. | ||
| 836 | * @returns {*} The copy or updated `destination`, if `destination` was specified. | ||
| 837 | * | ||
| 838 | * @example | ||
| 839 | <example module="copyExample"> | ||
| 840 | <file name="index.html"> | ||
| 841 | <div ng-controller="ExampleController"> | ||
| 842 | <form novalidate class="simple-form"> | ||
| 843 | <label>Name: <input type="text" ng-model="user.name" /></label><br /> | ||
| 844 | <label>Age: <input type="number" ng-model="user.age" /></label><br /> | ||
| 845 | Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label> | ||
| 846 | <label><input type="radio" ng-model="user.gender" value="female" />female</label><br /> | ||
| 847 | <button ng-click="reset()">RESET</button> | ||
| 848 | <button ng-click="update(user)">SAVE</button> | ||
| 849 | </form> | ||
| 850 | <pre>form = {{user | json}}</pre> | ||
| 851 | <pre>master = {{master | json}}</pre> | ||
| 852 | </div> | ||
| 853 | </file> | ||
| 854 | <file name="script.js"> | ||
| 855 | // Module: copyExample | ||
| 856 | angular. | ||
| 857 | module('copyExample', []). | ||
| 858 | controller('ExampleController', ['$scope', function($scope) { | ||
| 859 | $scope.master = {}; | ||
| 860 | |||
| 861 | $scope.reset = function() { | ||
| 862 | // Example with 1 argument | ||
| 863 | $scope.user = angular.copy($scope.master); | ||
| 864 | }; | ||
| 865 | |||
| 866 | $scope.update = function(user) { | ||
| 867 | // Example with 2 arguments | ||
| 868 | angular.copy(user, $scope.master); | ||
| 869 | }; | ||
| 870 | |||
| 871 | $scope.reset(); | ||
| 872 | }]); | ||
| 873 | </file> | ||
| 874 | </example> | ||
| 875 | */ | ||
| 876 | function copy(source, destination) { | ||
| 877 | var stackSource = []; | ||
| 878 | var stackDest = []; | ||
| 879 | |||
| 880 | if (destination) { | ||
| 881 | if (isTypedArray(destination) || isArrayBuffer(destination)) { | ||
| 882 | throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated."); | ||
| 883 | } | ||
| 884 | if (source === destination) { | ||
| 885 | throw ngMinErr('cpi', "Can't copy! Source and destination are identical."); | ||
| 886 | } | ||
| 887 | |||
| 888 | // Empty the destination object | ||
| 889 | if (isArray(destination)) { | ||
| 890 | destination.length = 0; | ||
| 891 | } else { | ||
| 892 | forEach(destination, function(value, key) { | ||
| 893 | if (key !== '$$hashKey') { | ||
| 894 | delete destination[key]; | ||
| 895 | } | ||
| 896 | }); | ||
| 897 | } | ||
| 898 | |||
| 899 | stackSource.push(source); | ||
| 900 | stackDest.push(destination); | ||
| 901 | return copyRecurse(source, destination); | ||
| 902 | } | ||
| 903 | |||
| 904 | return copyElement(source); | ||
| 905 | |||
| 906 | function copyRecurse(source, destination) { | ||
| 907 | var h = destination.$$hashKey; | ||
| 908 | var key; | ||
| 909 | if (isArray(source)) { | ||
| 910 | for (var i = 0, ii = source.length; i < ii; i++) { | ||
| 911 | destination.push(copyElement(source[i])); | ||
| 912 | } | ||
| 913 | } else if (isBlankObject(source)) { | ||
| 914 | // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty | ||
| 915 | for (key in source) { | ||
| 916 | destination[key] = copyElement(source[key]); | ||
| 917 | } | ||
| 918 | } else if (source && typeof source.hasOwnProperty === 'function') { | ||
| 919 | // Slow path, which must rely on hasOwnProperty | ||
| 920 | for (key in source) { | ||
| 921 | if (source.hasOwnProperty(key)) { | ||
| 922 | destination[key] = copyElement(source[key]); | ||
| 923 | } | ||
| 924 | } | ||
| 925 | } else { | ||
| 926 | // Slowest path --- hasOwnProperty can't be called as a method | ||
| 927 | for (key in source) { | ||
| 928 | if (hasOwnProperty.call(source, key)) { | ||
| 929 | destination[key] = copyElement(source[key]); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 933 | setHashKey(destination, h); | ||
| 934 | return destination; | ||
| 935 | } | ||
| 936 | |||
| 937 | function copyElement(source) { | ||
| 938 | // Simple values | ||
| 939 | if (!isObject(source)) { | ||
| 940 | return source; | ||
| 941 | } | ||
| 942 | |||
| 943 | // Already copied values | ||
| 944 | var index = stackSource.indexOf(source); | ||
| 945 | if (index !== -1) { | ||
| 946 | return stackDest[index]; | ||
| 947 | } | ||
| 948 | |||
| 949 | if (isWindow(source) || isScope(source)) { | ||
| 950 | throw ngMinErr('cpws', | ||
| 951 | "Can't copy! Making copies of Window or Scope instances is not supported."); | ||
| 952 | } | ||
| 953 | |||
| 954 | var needsRecurse = false; | ||
| 955 | var destination = copyType(source); | ||
| 956 | |||
| 957 | if (destination === undefined) { | ||
| 958 | destination = isArray(source) ? [] : Object.create(getPrototypeOf(source)); | ||
| 959 | needsRecurse = true; | ||
| 960 | } | ||
| 961 | |||
| 962 | stackSource.push(source); | ||
| 963 | stackDest.push(destination); | ||
| 964 | |||
| 965 | return needsRecurse | ||
| 966 | ? copyRecurse(source, destination) | ||
| 967 | : destination; | ||
| 968 | } | ||
| 969 | |||
| 970 | function copyType(source) { | ||
| 971 | switch (toString.call(source)) { | ||
| 972 | case '[object Int8Array]': | ||
| 973 | case '[object Int16Array]': | ||
| 974 | case '[object Int32Array]': | ||
| 975 | case '[object Float32Array]': | ||
| 976 | case '[object Float64Array]': | ||
| 977 | case '[object Uint8Array]': | ||
| 978 | case '[object Uint8ClampedArray]': | ||
| 979 | case '[object Uint16Array]': | ||
| 980 | case '[object Uint32Array]': | ||
| 981 | return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length); | ||
| 982 | |||
| 983 | case '[object ArrayBuffer]': | ||
| 984 | //Support: IE10 | ||
| 985 | if (!source.slice) { | ||
| 986 | var copied = new ArrayBuffer(source.byteLength); | ||
| 987 | new Uint8Array(copied).set(new Uint8Array(source)); | ||
| 988 | return copied; | ||
| 989 | } | ||
| 990 | return source.slice(0); | ||
| 991 | |||
| 992 | case '[object Boolean]': | ||
| 993 | case '[object Number]': | ||
| 994 | case '[object String]': | ||
| 995 | case '[object Date]': | ||
| 996 | return new source.constructor(source.valueOf()); | ||
| 997 | |||
| 998 | case '[object RegExp]': | ||
| 999 | var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); | ||
| 1000 | re.lastIndex = source.lastIndex; | ||
| 1001 | return re; | ||
| 1002 | |||
| 1003 | case '[object Blob]': | ||
| 1004 | return new source.constructor([source], {type: source.type}); | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | if (isFunction(source.cloneNode)) { | ||
| 1008 | return source.cloneNode(true); | ||
| 1009 | } | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | |||
| 1014 | /** | ||
| 1015 | * @ngdoc function | ||
| 1016 | * @name angular.equals | ||
| 1017 | * @module ng | ||
| 1018 | * @kind function | ||
| 1019 | * | ||
| 1020 | * @description | ||
| 1021 | * Determines if two objects or two values are equivalent. Supports value types, regular | ||
| 1022 | * expressions, arrays and objects. | ||
| 1023 | * | ||
| 1024 | * Two objects or values are considered equivalent if at least one of the following is true: | ||
| 1025 | * | ||
| 1026 | * * Both objects or values pass `===` comparison. | ||
| 1027 | * * Both objects or values are of the same type and all of their properties are equal by | ||
| 1028 | * comparing them with `angular.equals`. | ||
| 1029 | * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) | ||
| 1030 | * * Both values represent the same regular expression (In JavaScript, | ||
| 1031 | * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual | ||
| 1032 | * representation matches). | ||
| 1033 | * | ||
| 1034 | * During a property comparison, properties of `function` type and properties with names | ||
| 1035 | * that begin with `$` are ignored. | ||
| 1036 | * | ||
| 1037 | * Scope and DOMWindow objects are being compared only by identify (`===`). | ||
| 1038 | * | ||
| 1039 | * @param {*} o1 Object or value to compare. | ||
| 1040 | * @param {*} o2 Object or value to compare. | ||
| 1041 | * @returns {boolean} True if arguments are equal. | ||
| 1042 | * | ||
| 1043 | * @example | ||
| 1044 | <example module="equalsExample" name="equalsExample"> | ||
| 1045 | <file name="index.html"> | ||
| 1046 | <div ng-controller="ExampleController"> | ||
| 1047 | <form novalidate> | ||
| 1048 | <h3>User 1</h3> | ||
| 1049 | Name: <input type="text" ng-model="user1.name"> | ||
| 1050 | Age: <input type="number" ng-model="user1.age"> | ||
| 1051 | |||
| 1052 | <h3>User 2</h3> | ||
| 1053 | Name: <input type="text" ng-model="user2.name"> | ||
| 1054 | Age: <input type="number" ng-model="user2.age"> | ||
| 1055 | |||
| 1056 | <div> | ||
| 1057 | <br/> | ||
| 1058 | <input type="button" value="Compare" ng-click="compare()"> | ||
| 1059 | </div> | ||
| 1060 | User 1: <pre>{{user1 | json}}</pre> | ||
| 1061 | User 2: <pre>{{user2 | json}}</pre> | ||
| 1062 | Equal: <pre>{{result}}</pre> | ||
| 1063 | </form> | ||
| 1064 | </div> | ||
| 1065 | </file> | ||
| 1066 | <file name="script.js"> | ||
| 1067 | angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) { | ||
| 1068 | $scope.user1 = {}; | ||
| 1069 | $scope.user2 = {}; | ||
| 1070 | $scope.result; | ||
| 1071 | $scope.compare = function() { | ||
| 1072 | $scope.result = angular.equals($scope.user1, $scope.user2); | ||
| 1073 | }; | ||
| 1074 | }]); | ||
| 1075 | </file> | ||
| 1076 | </example> | ||
| 1077 | */ | ||
| 1078 | function equals(o1, o2) { | ||
| 1079 | if (o1 === o2) return true; | ||
| 1080 | if (o1 === null || o2 === null) return false; | ||
| 1081 | if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN | ||
| 1082 | var t1 = typeof o1, t2 = typeof o2, length, key, keySet; | ||
| 1083 | if (t1 == t2 && t1 == 'object') { | ||
| 1084 | if (isArray(o1)) { | ||
| 1085 | if (!isArray(o2)) return false; | ||
| 1086 | if ((length = o1.length) == o2.length) { | ||
| 1087 | for (key = 0; key < length; key++) { | ||
| 1088 | if (!equals(o1[key], o2[key])) return false; | ||
| 1089 | } | ||
| 1090 | return true; | ||
| 1091 | } | ||
| 1092 | } else if (isDate(o1)) { | ||
| 1093 | if (!isDate(o2)) return false; | ||
| 1094 | return equals(o1.getTime(), o2.getTime()); | ||
| 1095 | } else if (isRegExp(o1)) { | ||
| 1096 | if (!isRegExp(o2)) return false; | ||
| 1097 | return o1.toString() == o2.toString(); | ||
| 1098 | } else { | ||
| 1099 | if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || | ||
| 1100 | isArray(o2) || isDate(o2) || isRegExp(o2)) return false; | ||
| 1101 | keySet = createMap(); | ||
| 1102 | for (key in o1) { | ||
| 1103 | if (key.charAt(0) === '$' || isFunction(o1[key])) continue; | ||
| 1104 | if (!equals(o1[key], o2[key])) return false; | ||
| 1105 | keySet[key] = true; | ||
| 1106 | } | ||
| 1107 | for (key in o2) { | ||
| 1108 | if (!(key in keySet) && | ||
| 1109 | key.charAt(0) !== '$' && | ||
| 1110 | isDefined(o2[key]) && | ||
| 1111 | !isFunction(o2[key])) return false; | ||
| 1112 | } | ||
| 1113 | return true; | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | return false; | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | var csp = function() { | ||
| 1120 | if (!isDefined(csp.rules)) { | ||
| 1121 | |||
| 1122 | |||
| 1123 | var ngCspElement = (window.document.querySelector('[ng-csp]') || | ||
| 1124 | window.document.querySelector('[data-ng-csp]')); | ||
| 1125 | |||
| 1126 | if (ngCspElement) { | ||
| 1127 | var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || | ||
| 1128 | ngCspElement.getAttribute('data-ng-csp'); | ||
| 1129 | csp.rules = { | ||
| 1130 | noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), | ||
| 1131 | noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) | ||
| 1132 | }; | ||
| 1133 | } else { | ||
| 1134 | csp.rules = { | ||
| 1135 | noUnsafeEval: noUnsafeEval(), | ||
| 1136 | noInlineStyle: false | ||
| 1137 | }; | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | return csp.rules; | ||
| 1142 | |||
| 1143 | function noUnsafeEval() { | ||
| 1144 | try { | ||
| 1145 | /* jshint -W031, -W054 */ | ||
| 1146 | new Function(''); | ||
| 1147 | /* jshint +W031, +W054 */ | ||
| 1148 | return false; | ||
| 1149 | } catch (e) { | ||
| 1150 | return true; | ||
| 1151 | } | ||
| 1152 | } | ||
| 1153 | }; | ||
| 1154 | |||
| 1155 | /** | ||
| 1156 | * @ngdoc directive | ||
| 1157 | * @module ng | ||
| 1158 | * @name ngJq | ||
| 1159 | * | ||
| 1160 | * @element ANY | ||
| 1161 | * @param {string=} ngJq the name of the library available under `window` | ||
| 1162 | * to be used for angular.element | ||
| 1163 | * @description | ||
| 1164 | * Use this directive to force the angular.element library. This should be | ||
| 1165 | * used to force either jqLite by leaving ng-jq blank or setting the name of | ||
| 1166 | * the jquery variable under window (eg. jQuery). | ||
| 1167 | * | ||
| 1168 | * Since angular looks for this directive when it is loaded (doesn't wait for the | ||
| 1169 | * DOMContentLoaded event), it must be placed on an element that comes before the script | ||
| 1170 | * which loads angular. Also, only the first instance of `ng-jq` will be used and all | ||
| 1171 | * others ignored. | ||
| 1172 | * | ||
| 1173 | * @example | ||
| 1174 | * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. | ||
| 1175 | ```html | ||
| 1176 | <!doctype html> | ||
| 1177 | <html ng-app ng-jq> | ||
| 1178 | ... | ||
| 1179 | ... | ||
| 1180 | </html> | ||
| 1181 | ``` | ||
| 1182 | * @example | ||
| 1183 | * This example shows how to use a jQuery based library of a different name. | ||
| 1184 | * The library name must be available at the top most 'window'. | ||
| 1185 | ```html | ||
| 1186 | <!doctype html> | ||
| 1187 | <html ng-app ng-jq="jQueryLib"> | ||
| 1188 | ... | ||
| 1189 | ... | ||
| 1190 | </html> | ||
| 1191 | ``` | ||
| 1192 | */ | ||
| 1193 | var jq = function() { | ||
| 1194 | if (isDefined(jq.name_)) return jq.name_; | ||
| 1195 | var el; | ||
| 1196 | var i, ii = ngAttrPrefixes.length, prefix, name; | ||
| 1197 | for (i = 0; i < ii; ++i) { | ||
| 1198 | prefix = ngAttrPrefixes[i]; | ||
| 1199 | if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) { | ||
| 1200 | name = el.getAttribute(prefix + 'jq'); | ||
| 1201 | break; | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | return (jq.name_ = name); | ||
| 1206 | }; | ||
| 1207 | |||
| 1208 | function concat(array1, array2, index) { | ||
| 1209 | return array1.concat(slice.call(array2, index)); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | function sliceArgs(args, startIndex) { | ||
| 1213 | return slice.call(args, startIndex || 0); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | |||
| 1217 | /* jshint -W101 */ | ||
| 1218 | /** | ||
| 1219 | * @ngdoc function | ||
| 1220 | * @name angular.bind | ||
| 1221 | * @module ng | ||
| 1222 | * @kind function | ||
| 1223 | * | ||
| 1224 | * @description | ||
| 1225 | * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for | ||
| 1226 | * `fn`). You can supply optional `args` that are prebound to the function. This feature is also | ||
| 1227 | * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as | ||
| 1228 | * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). | ||
| 1229 | * | ||
| 1230 | * @param {Object} self Context which `fn` should be evaluated in. | ||
| 1231 | * @param {function()} fn Function to be bound. | ||
| 1232 | * @param {...*} args Optional arguments to be prebound to the `fn` function call. | ||
| 1233 | * @returns {function()} Function that wraps the `fn` with all the specified bindings. | ||
| 1234 | */ | ||
| 1235 | /* jshint +W101 */ | ||
| 1236 | function bind(self, fn) { | ||
| 1237 | var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; | ||
| 1238 | if (isFunction(fn) && !(fn instanceof RegExp)) { | ||
| 1239 | return curryArgs.length | ||
| 1240 | ? function() { | ||
| 1241 | return arguments.length | ||
| 1242 | ? fn.apply(self, concat(curryArgs, arguments, 0)) | ||
| 1243 | : fn.apply(self, curryArgs); | ||
| 1244 | } | ||
| 1245 | : function() { | ||
| 1246 | return arguments.length | ||
| 1247 | ? fn.apply(self, arguments) | ||
| 1248 | : fn.call(self); | ||
| 1249 | }; | ||
| 1250 | } else { | ||
| 1251 | // In IE, native methods are not functions so they cannot be bound (note: they don't need to be). | ||
| 1252 | return fn; | ||
| 1253 | } | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | |||
| 1257 | function toJsonReplacer(key, value) { | ||
| 1258 | var val = value; | ||
| 1259 | |||
| 1260 | if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { | ||
| 1261 | val = undefined; | ||
| 1262 | } else if (isWindow(value)) { | ||
| 1263 | val = '$WINDOW'; | ||
| 1264 | } else if (value && window.document === value) { | ||
| 1265 | val = '$DOCUMENT'; | ||
| 1266 | } else if (isScope(value)) { | ||
| 1267 | val = '$SCOPE'; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | return val; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | |||
| 1274 | /** | ||
| 1275 | * @ngdoc function | ||
| 1276 | * @name angular.toJson | ||
| 1277 | * @module ng | ||
| 1278 | * @kind function | ||
| 1279 | * | ||
| 1280 | * @description | ||
| 1281 | * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be | ||
| 1282 | * stripped since angular uses this notation internally. | ||
| 1283 | * | ||
| 1284 | * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. | ||
| 1285 | * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. | ||
| 1286 | * If set to an integer, the JSON output will contain that many spaces per indentation. | ||
| 1287 | * @returns {string|undefined} JSON-ified string representing `obj`. | ||
| 1288 | * @knownIssue | ||
| 1289 | * | ||
| 1290 | * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date` | ||
| 1291 | * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the | ||
| 1292 | * `Date.prototype.toJSON` method as follows: | ||
| 1293 | * | ||
| 1294 | * ``` | ||
| 1295 | * var _DatetoJSON = Date.prototype.toJSON; | ||
| 1296 | * Date.prototype.toJSON = function() { | ||
| 1297 | * try { | ||
| 1298 | * return _DatetoJSON.call(this); | ||
| 1299 | * } catch(e) { | ||
| 1300 | * if (e instanceof RangeError) { | ||
| 1301 | * return null; | ||
| 1302 | * } | ||
| 1303 | * throw e; | ||
| 1304 | * } | ||
| 1305 | * }; | ||
| 1306 | * ``` | ||
| 1307 | * | ||
| 1308 | * See https://github.com/angular/angular.js/pull/14221 for more information. | ||
| 1309 | */ | ||
| 1310 | function toJson(obj, pretty) { | ||
| 1311 | if (isUndefined(obj)) return undefined; | ||
| 1312 | if (!isNumber(pretty)) { | ||
| 1313 | pretty = pretty ? 2 : null; | ||
| 1314 | } | ||
| 1315 | return JSON.stringify(obj, toJsonReplacer, pretty); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | |||
| 1319 | /** | ||
| 1320 | * @ngdoc function | ||
| 1321 | * @name angular.fromJson | ||
| 1322 | * @module ng | ||
| 1323 | * @kind function | ||
| 1324 | * | ||
| 1325 | * @description | ||
| 1326 | * Deserializes a JSON string. | ||
| 1327 | * | ||
| 1328 | * @param {string} json JSON string to deserialize. | ||
| 1329 | * @returns {Object|Array|string|number} Deserialized JSON string. | ||
| 1330 | */ | ||
| 1331 | function fromJson(json) { | ||
| 1332 | return isString(json) | ||
| 1333 | ? JSON.parse(json) | ||
| 1334 | : json; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | |||
| 1338 | var ALL_COLONS = /:/g; | ||
| 1339 | function timezoneToOffset(timezone, fallback) { | ||
| 1340 | // IE/Edge do not "understand" colon (`:`) in timezone | ||
| 1341 | timezone = timezone.replace(ALL_COLONS, ''); | ||
| 1342 | var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; | ||
| 1343 | return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | |||
| 1347 | function addDateMinutes(date, minutes) { | ||
| 1348 | date = new Date(date.getTime()); | ||
| 1349 | date.setMinutes(date.getMinutes() + minutes); | ||
| 1350 | return date; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | |||
| 1354 | function convertTimezoneToLocal(date, timezone, reverse) { | ||
| 1355 | reverse = reverse ? -1 : 1; | ||
| 1356 | var dateTimezoneOffset = date.getTimezoneOffset(); | ||
| 1357 | var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); | ||
| 1358 | return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | |||
| 1362 | /** | ||
| 1363 | * @returns {string} Returns the string representation of the element. | ||
| 1364 | */ | ||
| 1365 | function startingTag(element) { | ||
| 1366 | element = jqLite(element).clone(); | ||
| 1367 | try { | ||
| 1368 | // turns out IE does not let you set .html() on elements which | ||
| 1369 | // are not allowed to have children. So we just ignore it. | ||
| 1370 | element.empty(); | ||
| 1371 | } catch (e) {} | ||
| 1372 | var elemHtml = jqLite('<div>').append(element).html(); | ||
| 1373 | try { | ||
| 1374 | return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : | ||
| 1375 | elemHtml. | ||
| 1376 | match(/^(<[^>]+>)/)[1]. | ||
| 1377 | replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);}); | ||
| 1378 | } catch (e) { | ||
| 1379 | return lowercase(elemHtml); | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | } | ||
| 1383 | |||
| 1384 | |||
| 1385 | ///////////////////////////////////////////////// | ||
| 1386 | |||
| 1387 | /** | ||
| 1388 | * Tries to decode the URI component without throwing an exception. | ||
| 1389 | * | ||
| 1390 | * @private | ||
| 1391 | * @param str value potential URI component to check. | ||
| 1392 | * @returns {boolean} True if `value` can be decoded | ||
| 1393 | * with the decodeURIComponent function. | ||
| 1394 | */ | ||
| 1395 | function tryDecodeURIComponent(value) { | ||
| 1396 | try { | ||
| 1397 | return decodeURIComponent(value); | ||
| 1398 | } catch (e) { | ||
| 1399 | // Ignore any invalid uri component. | ||
| 1400 | } | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | |||
| 1404 | /** | ||
| 1405 | * Parses an escaped url query string into key-value pairs. | ||
| 1406 | * @returns {Object.<string,boolean|Array>} | ||
| 1407 | */ | ||
| 1408 | function parseKeyValue(/**string*/keyValue) { | ||
| 1409 | var obj = {}; | ||
| 1410 | forEach((keyValue || "").split('&'), function(keyValue) { | ||
| 1411 | var splitPoint, key, val; | ||
| 1412 | if (keyValue) { | ||
| 1413 | key = keyValue = keyValue.replace(/\+/g,'%20'); | ||
| 1414 | splitPoint = keyValue.indexOf('='); | ||
| 1415 | if (splitPoint !== -1) { | ||
| 1416 | key = keyValue.substring(0, splitPoint); | ||
| 1417 | val = keyValue.substring(splitPoint + 1); | ||
| 1418 | } | ||
| 1419 | key = tryDecodeURIComponent(key); | ||
| 1420 | if (isDefined(key)) { | ||
| 1421 | val = isDefined(val) ? tryDecodeURIComponent(val) : true; | ||
| 1422 | if (!hasOwnProperty.call(obj, key)) { | ||
| 1423 | obj[key] = val; | ||
| 1424 | } else if (isArray(obj[key])) { | ||
| 1425 | obj[key].push(val); | ||
| 1426 | } else { | ||
| 1427 | obj[key] = [obj[key],val]; | ||
| 1428 | } | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | }); | ||
| 1432 | return obj; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | function toKeyValue(obj) { | ||
| 1436 | var parts = []; | ||
| 1437 | forEach(obj, function(value, key) { | ||
| 1438 | if (isArray(value)) { | ||
| 1439 | forEach(value, function(arrayValue) { | ||
| 1440 | parts.push(encodeUriQuery(key, true) + | ||
| 1441 | (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); | ||
| 1442 | }); | ||
| 1443 | } else { | ||
| 1444 | parts.push(encodeUriQuery(key, true) + | ||
| 1445 | (value === true ? '' : '=' + encodeUriQuery(value, true))); | ||
| 1446 | } | ||
| 1447 | }); | ||
| 1448 | return parts.length ? parts.join('&') : ''; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | |||
| 1452 | /** | ||
| 1453 | * We need our custom method because encodeURIComponent is too aggressive and doesn't follow | ||
| 1454 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path | ||
| 1455 | * segments: | ||
| 1456 | * segment = *pchar | ||
| 1457 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
| 1458 | * pct-encoded = "%" HEXDIG HEXDIG | ||
| 1459 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | ||
| 1460 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | ||
| 1461 | * / "*" / "+" / "," / ";" / "=" | ||
| 1462 | */ | ||
| 1463 | function encodeUriSegment(val) { | ||
| 1464 | return encodeUriQuery(val, true). | ||
| 1465 | replace(/%26/gi, '&'). | ||
| 1466 | replace(/%3D/gi, '='). | ||
| 1467 | replace(/%2B/gi, '+'); | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | |||
| 1471 | /** | ||
| 1472 | * This method is intended for encoding *key* or *value* parts of query component. We need a custom | ||
| 1473 | * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be | ||
| 1474 | * encoded per http://tools.ietf.org/html/rfc3986: | ||
| 1475 | * query = *( pchar / "/" / "?" ) | ||
| 1476 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
| 1477 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | ||
| 1478 | * pct-encoded = "%" HEXDIG HEXDIG | ||
| 1479 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | ||
| 1480 | * / "*" / "+" / "," / ";" / "=" | ||
| 1481 | */ | ||
| 1482 | function encodeUriQuery(val, pctEncodeSpaces) { | ||
| 1483 | return encodeURIComponent(val). | ||
| 1484 | replace(/%40/gi, '@'). | ||
| 1485 | replace(/%3A/gi, ':'). | ||
| 1486 | replace(/%24/g, '$'). | ||
| 1487 | replace(/%2C/gi, ','). | ||
| 1488 | replace(/%3B/gi, ';'). | ||
| 1489 | replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; | ||
| 1493 | |||
| 1494 | function getNgAttribute(element, ngAttr) { | ||
| 1495 | var attr, i, ii = ngAttrPrefixes.length; | ||
| 1496 | for (i = 0; i < ii; ++i) { | ||
| 1497 | attr = ngAttrPrefixes[i] + ngAttr; | ||
| 1498 | if (isString(attr = element.getAttribute(attr))) { | ||
| 1499 | return attr; | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | return null; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | /** | ||
| 1506 | * @ngdoc directive | ||
| 1507 | * @name ngApp | ||
| 1508 | * @module ng | ||
| 1509 | * | ||
| 1510 | * @element ANY | ||
| 1511 | * @param {angular.Module} ngApp an optional application | ||
| 1512 | * {@link angular.module module} name to load. | ||
| 1513 | * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be | ||
| 1514 | * created in "strict-di" mode. This means that the application will fail to invoke functions which | ||
| 1515 | * do not use explicit function annotation (and are thus unsuitable for minification), as described | ||
| 1516 | * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in | ||
| 1517 | * tracking down the root of these bugs. | ||
| 1518 | * | ||
| 1519 | * @description | ||
| 1520 | * | ||
| 1521 | * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive | ||
| 1522 | * designates the **root element** of the application and is typically placed near the root element | ||
| 1523 | * of the page - e.g. on the `<body>` or `<html>` tags. | ||
| 1524 | * | ||
| 1525 | * There are a few things to keep in mind when using `ngApp`: | ||
| 1526 | * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` | ||
| 1527 | * found in the document will be used to define the root element to auto-bootstrap as an | ||
| 1528 | * application. To run multiple applications in an HTML document you must manually bootstrap them using | ||
| 1529 | * {@link angular.bootstrap} instead. | ||
| 1530 | * - AngularJS applications cannot be nested within each other. | ||
| 1531 | * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`. | ||
| 1532 | * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and | ||
| 1533 | * {@link ngRoute.ngView `ngView`}. | ||
| 1534 | * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, | ||
| 1535 | * causing animations to stop working and making the injector inaccessible from outside the app. | ||
| 1536 | * | ||
| 1537 | * You can specify an **AngularJS module** to be used as the root module for the application. This | ||
| 1538 | * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It | ||
| 1539 | * should contain the application code needed or have dependencies on other modules that will | ||
| 1540 | * contain the code. See {@link angular.module} for more information. | ||
| 1541 | * | ||
| 1542 | * In the example below if the `ngApp` directive were not placed on the `html` element then the | ||
| 1543 | * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` | ||
| 1544 | * would not be resolved to `3`. | ||
| 1545 | * | ||
| 1546 | * `ngApp` is the easiest, and most common way to bootstrap an application. | ||
| 1547 | * | ||
| 1548 | <example module="ngAppDemo"> | ||
| 1549 | <file name="index.html"> | ||
| 1550 | <div ng-controller="ngAppDemoController"> | ||
| 1551 | I can add: {{a}} + {{b}} = {{ a+b }} | ||
| 1552 | </div> | ||
| 1553 | </file> | ||
| 1554 | <file name="script.js"> | ||
| 1555 | angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { | ||
| 1556 | $scope.a = 1; | ||
| 1557 | $scope.b = 2; | ||
| 1558 | }); | ||
| 1559 | </file> | ||
| 1560 | </example> | ||
| 1561 | * | ||
| 1562 | * Using `ngStrictDi`, you would see something like this: | ||
| 1563 | * | ||
| 1564 | <example ng-app-included="true"> | ||
| 1565 | <file name="index.html"> | ||
| 1566 | <div ng-app="ngAppStrictDemo" ng-strict-di> | ||
| 1567 | <div ng-controller="GoodController1"> | ||
| 1568 | I can add: {{a}} + {{b}} = {{ a+b }} | ||
| 1569 | |||
| 1570 | <p>This renders because the controller does not fail to | ||
| 1571 | instantiate, by using explicit annotation style (see | ||
| 1572 | script.js for details) | ||
| 1573 | </p> | ||
| 1574 | </div> | ||
| 1575 | |||
| 1576 | <div ng-controller="GoodController2"> | ||
| 1577 | Name: <input ng-model="name"><br /> | ||
| 1578 | Hello, {{name}}! | ||
| 1579 | |||
| 1580 | <p>This renders because the controller does not fail to | ||
| 1581 | instantiate, by using explicit annotation style | ||
| 1582 | (see script.js for details) | ||
| 1583 | </p> | ||
| 1584 | </div> | ||
| 1585 | |||
| 1586 | <div ng-controller="BadController"> | ||
| 1587 | I can add: {{a}} + {{b}} = {{ a+b }} | ||
| 1588 | |||
| 1589 | <p>The controller could not be instantiated, due to relying | ||
| 1590 | on automatic function annotations (which are disabled in | ||
| 1591 | strict mode). As such, the content of this section is not | ||
| 1592 | interpolated, and there should be an error in your web console. | ||
| 1593 | </p> | ||
| 1594 | </div> | ||
| 1595 | </div> | ||
| 1596 | </file> | ||
| 1597 | <file name="script.js"> | ||
| 1598 | angular.module('ngAppStrictDemo', []) | ||
| 1599 | // BadController will fail to instantiate, due to relying on automatic function annotation, | ||
| 1600 | // rather than an explicit annotation | ||
| 1601 | .controller('BadController', function($scope) { | ||
| 1602 | $scope.a = 1; | ||
| 1603 | $scope.b = 2; | ||
| 1604 | }) | ||
| 1605 | // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, | ||
| 1606 | // due to using explicit annotations using the array style and $inject property, respectively. | ||
| 1607 | .controller('GoodController1', ['$scope', function($scope) { | ||
| 1608 | $scope.a = 1; | ||
| 1609 | $scope.b = 2; | ||
| 1610 | }]) | ||
| 1611 | .controller('GoodController2', GoodController2); | ||
| 1612 | function GoodController2($scope) { | ||
| 1613 | $scope.name = "World"; | ||
| 1614 | } | ||
| 1615 | GoodController2.$inject = ['$scope']; | ||
| 1616 | </file> | ||
| 1617 | <file name="style.css"> | ||
| 1618 | div[ng-controller] { | ||
| 1619 | margin-bottom: 1em; | ||
| 1620 | -webkit-border-radius: 4px; | ||
| 1621 | border-radius: 4px; | ||
| 1622 | border: 1px solid; | ||
| 1623 | padding: .5em; | ||
| 1624 | } | ||
| 1625 | div[ng-controller^=Good] { | ||
| 1626 | border-color: #d6e9c6; | ||
| 1627 | background-color: #dff0d8; | ||
| 1628 | color: #3c763d; | ||
| 1629 | } | ||
| 1630 | div[ng-controller^=Bad] { | ||
| 1631 | border-color: #ebccd1; | ||
| 1632 | background-color: #f2dede; | ||
| 1633 | color: #a94442; | ||
| 1634 | margin-bottom: 0; | ||
| 1635 | } | ||
| 1636 | </file> | ||
| 1637 | </example> | ||
| 1638 | */ | ||
| 1639 | function angularInit(element, bootstrap) { | ||
| 1640 | var appElement, | ||
| 1641 | module, | ||
| 1642 | config = {}; | ||
| 1643 | |||
| 1644 | // The element `element` has priority over any other element. | ||
| 1645 | forEach(ngAttrPrefixes, function(prefix) { | ||
| 1646 | var name = prefix + 'app'; | ||
| 1647 | |||
| 1648 | if (!appElement && element.hasAttribute && element.hasAttribute(name)) { | ||
| 1649 | appElement = element; | ||
| 1650 | module = element.getAttribute(name); | ||
| 1651 | } | ||
| 1652 | }); | ||
| 1653 | forEach(ngAttrPrefixes, function(prefix) { | ||
| 1654 | var name = prefix + 'app'; | ||
| 1655 | var candidate; | ||
| 1656 | |||
| 1657 | if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { | ||
| 1658 | appElement = candidate; | ||
| 1659 | module = candidate.getAttribute(name); | ||
| 1660 | } | ||
| 1661 | }); | ||
| 1662 | if (appElement) { | ||
| 1663 | config.strictDi = getNgAttribute(appElement, "strict-di") !== null; | ||
| 1664 | bootstrap(appElement, module ? [module] : [], config); | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | /** | ||
| 1669 | * @ngdoc function | ||
| 1670 | * @name angular.bootstrap | ||
| 1671 | * @module ng | ||
| 1672 | * @description | ||
| 1673 | * Use this function to manually start up angular application. | ||
| 1674 | * | ||
| 1675 | * For more information, see the {@link guide/bootstrap Bootstrap guide}. | ||
| 1676 | * | ||
| 1677 | * Angular will detect if it has been loaded into the browser more than once and only allow the | ||
| 1678 | * first loaded script to be bootstrapped and will report a warning to the browser console for | ||
| 1679 | * each of the subsequent scripts. This prevents strange results in applications, where otherwise | ||
| 1680 | * multiple instances of Angular try to work on the DOM. | ||
| 1681 | * | ||
| 1682 | * <div class="alert alert-warning"> | ||
| 1683 | * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually. | ||
| 1684 | * They must use {@link ng.directive:ngApp ngApp}. | ||
| 1685 | * </div> | ||
| 1686 | * | ||
| 1687 | * <div class="alert alert-warning"> | ||
| 1688 | * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, | ||
| 1689 | * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}. | ||
| 1690 | * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, | ||
| 1691 | * causing animations to stop working and making the injector inaccessible from outside the app. | ||
| 1692 | * </div> | ||
| 1693 | * | ||
| 1694 | * ```html | ||
| 1695 | * <!doctype html> | ||
| 1696 | * <html> | ||
| 1697 | * <body> | ||
| 1698 | * <div ng-controller="WelcomeController"> | ||
| 1699 | * {{greeting}} | ||
| 1700 | * </div> | ||
| 1701 | * | ||
| 1702 | * <script src="angular.js"></script> | ||
| 1703 | * <script> | ||
| 1704 | * var app = angular.module('demo', []) | ||
| 1705 | * .controller('WelcomeController', function($scope) { | ||
| 1706 | * $scope.greeting = 'Welcome!'; | ||
| 1707 | * }); | ||
| 1708 | * angular.bootstrap(document, ['demo']); | ||
| 1709 | * </script> | ||
| 1710 | * </body> | ||
| 1711 | * </html> | ||
| 1712 | * ``` | ||
| 1713 | * | ||
| 1714 | * @param {DOMElement} element DOM element which is the root of angular application. | ||
| 1715 | * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. | ||
| 1716 | * Each item in the array should be the name of a predefined module or a (DI annotated) | ||
| 1717 | * function that will be invoked by the injector as a `config` block. | ||
| 1718 | * See: {@link angular.module modules} | ||
| 1719 | * @param {Object=} config an object for defining configuration options for the application. The | ||
| 1720 | * following keys are supported: | ||
| 1721 | * | ||
| 1722 | * * `strictDi` - disable automatic function annotation for the application. This is meant to | ||
| 1723 | * assist in finding bugs which break minified code. Defaults to `false`. | ||
| 1724 | * | ||
| 1725 | * @returns {auto.$injector} Returns the newly created injector for this app. | ||
| 1726 | */ | ||
| 1727 | function bootstrap(element, modules, config) { | ||
| 1728 | if (!isObject(config)) config = {}; | ||
| 1729 | var defaultConfig = { | ||
| 1730 | strictDi: false | ||
| 1731 | }; | ||
| 1732 | config = extend(defaultConfig, config); | ||
| 1733 | var doBootstrap = function() { | ||
| 1734 | element = jqLite(element); | ||
| 1735 | |||
| 1736 | if (element.injector()) { | ||
| 1737 | var tag = (element[0] === window.document) ? 'document' : startingTag(element); | ||
| 1738 | // Encode angle brackets to prevent input from being sanitized to empty string #8683. | ||
| 1739 | throw ngMinErr( | ||
| 1740 | 'btstrpd', | ||
| 1741 | "App already bootstrapped with this element '{0}'", | ||
| 1742 | tag.replace(/</,'<').replace(/>/,'>')); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | modules = modules || []; | ||
| 1746 | modules.unshift(['$provide', function($provide) { | ||
| 1747 | $provide.value('$rootElement', element); | ||
| 1748 | }]); | ||
| 1749 | |||
| 1750 | if (config.debugInfoEnabled) { | ||
| 1751 | // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. | ||
| 1752 | modules.push(['$compileProvider', function($compileProvider) { | ||
| 1753 | $compileProvider.debugInfoEnabled(true); | ||
| 1754 | }]); | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | modules.unshift('ng'); | ||
| 1758 | var injector = createInjector(modules, config.strictDi); | ||
| 1759 | injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', | ||
| 1760 | function bootstrapApply(scope, element, compile, injector) { | ||
| 1761 | scope.$apply(function() { | ||
| 1762 | element.data('$injector', injector); | ||
| 1763 | compile(element)(scope); | ||
| 1764 | }); | ||
| 1765 | }] | ||
| 1766 | ); | ||
| 1767 | return injector; | ||
| 1768 | }; | ||
| 1769 | |||
| 1770 | var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; | ||
| 1771 | var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; | ||
| 1772 | |||
| 1773 | if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { | ||
| 1774 | config.debugInfoEnabled = true; | ||
| 1775 | window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { | ||
| 1779 | return doBootstrap(); | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); | ||
| 1783 | angular.resumeBootstrap = function(extraModules) { | ||
| 1784 | forEach(extraModules, function(module) { | ||
| 1785 | modules.push(module); | ||
| 1786 | }); | ||
| 1787 | return doBootstrap(); | ||
| 1788 | }; | ||
| 1789 | |||
| 1790 | if (isFunction(angular.resumeDeferredBootstrap)) { | ||
| 1791 | angular.resumeDeferredBootstrap(); | ||
| 1792 | } | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | /** | ||
| 1796 | * @ngdoc function | ||
| 1797 | * @name angular.reloadWithDebugInfo | ||
| 1798 | * @module ng | ||
| 1799 | * @description | ||
| 1800 | * Use this function to reload the current application with debug information turned on. | ||
| 1801 | * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. | ||
| 1802 | * | ||
| 1803 | * See {@link ng.$compileProvider#debugInfoEnabled} for more. | ||
| 1804 | */ | ||
| 1805 | function reloadWithDebugInfo() { | ||
| 1806 | window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; | ||
| 1807 | window.location.reload(); | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | /** | ||
| 1811 | * @name angular.getTestability | ||
| 1812 | * @module ng | ||
| 1813 | * @description | ||
| 1814 | * Get the testability service for the instance of Angular on the given | ||
| 1815 | * element. | ||
| 1816 | * @param {DOMElement} element DOM element which is the root of angular application. | ||
| 1817 | */ | ||
| 1818 | function getTestability(rootElement) { | ||
| 1819 | var injector = angular.element(rootElement).injector(); | ||
| 1820 | if (!injector) { | ||
| 1821 | throw ngMinErr('test', | ||
| 1822 | 'no injector found for element argument to getTestability'); | ||
| 1823 | } | ||
| 1824 | return injector.get('$$testability'); | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | var SNAKE_CASE_REGEXP = /[A-Z]/g; | ||
| 1828 | function snake_case(name, separator) { | ||
| 1829 | separator = separator || '_'; | ||
| 1830 | return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { | ||
| 1831 | return (pos ? separator : '') + letter.toLowerCase(); | ||
| 1832 | }); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | var bindJQueryFired = false; | ||
| 1836 | function bindJQuery() { | ||
| 1837 | var originalCleanData; | ||
| 1838 | |||
| 1839 | if (bindJQueryFired) { | ||
| 1840 | return; | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | // bind to jQuery if present; | ||
| 1844 | var jqName = jq(); | ||
| 1845 | jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present) | ||
| 1846 | !jqName ? undefined : // use jqLite | ||
| 1847 | window[jqName]; // use jQuery specified by `ngJq` | ||
| 1848 | |||
| 1849 | // Use jQuery if it exists with proper functionality, otherwise default to us. | ||
| 1850 | // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. | ||
| 1851 | // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older | ||
| 1852 | // versions. It will not work for sure with jQuery <1.7, though. | ||
| 1853 | if (jQuery && jQuery.fn.on) { | ||
| 1854 | jqLite = jQuery; | ||
| 1855 | extend(jQuery.fn, { | ||
| 1856 | scope: JQLitePrototype.scope, | ||
| 1857 | isolateScope: JQLitePrototype.isolateScope, | ||
| 1858 | controller: JQLitePrototype.controller, | ||
| 1859 | injector: JQLitePrototype.injector, | ||
| 1860 | inheritedData: JQLitePrototype.inheritedData | ||
| 1861 | }); | ||
| 1862 | |||
| 1863 | // All nodes removed from the DOM via various jQuery APIs like .remove() | ||
| 1864 | // are passed through jQuery.cleanData. Monkey-patch this method to fire | ||
| 1865 | // the $destroy event on all removed nodes. | ||
| 1866 | originalCleanData = jQuery.cleanData; | ||
| 1867 | jQuery.cleanData = function(elems) { | ||
| 1868 | var events; | ||
| 1869 | for (var i = 0, elem; (elem = elems[i]) != null; i++) { | ||
| 1870 | events = jQuery._data(elem, "events"); | ||
| 1871 | if (events && events.$destroy) { | ||
| 1872 | jQuery(elem).triggerHandler('$destroy'); | ||
| 1873 | } | ||
| 1874 | } | ||
| 1875 | originalCleanData(elems); | ||
| 1876 | }; | ||
| 1877 | } else { | ||
| 1878 | jqLite = JQLite; | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | angular.element = jqLite; | ||
| 1882 | |||
| 1883 | // Prevent double-proxying. | ||
| 1884 | bindJQueryFired = true; | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | /** | ||
| 1888 | * throw error if the argument is falsy. | ||
| 1889 | */ | ||
| 1890 | function assertArg(arg, name, reason) { | ||
| 1891 | if (!arg) { | ||
| 1892 | throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); | ||
| 1893 | } | ||
| 1894 | return arg; | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | function assertArgFn(arg, name, acceptArrayAnnotation) { | ||
| 1898 | if (acceptArrayAnnotation && isArray(arg)) { | ||
| 1899 | arg = arg[arg.length - 1]; | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | assertArg(isFunction(arg), name, 'not a function, got ' + | ||
| 1903 | (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); | ||
| 1904 | return arg; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | /** | ||
| 1908 | * throw error if the name given is hasOwnProperty | ||
| 1909 | * @param {String} name the name to test | ||
| 1910 | * @param {String} context the context in which the name is used, such as module or directive | ||
| 1911 | */ | ||
| 1912 | function assertNotHasOwnProperty(name, context) { | ||
| 1913 | if (name === 'hasOwnProperty') { | ||
| 1914 | throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); | ||
| 1915 | } | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | /** | ||
| 1919 | * Return the value accessible from the object by path. Any undefined traversals are ignored | ||
| 1920 | * @param {Object} obj starting object | ||
| 1921 | * @param {String} path path to traverse | ||
| 1922 | * @param {boolean} [bindFnToScope=true] | ||
| 1923 | * @returns {Object} value as accessible by path | ||
| 1924 | */ | ||
| 1925 | //TODO(misko): this function needs to be removed | ||
| 1926 | function getter(obj, path, bindFnToScope) { | ||
| 1927 | if (!path) return obj; | ||
| 1928 | var keys = path.split('.'); | ||
| 1929 | var key; | ||
| 1930 | var lastInstance = obj; | ||
| 1931 | var len = keys.length; | ||
| 1932 | |||
| 1933 | for (var i = 0; i < len; i++) { | ||
| 1934 | key = keys[i]; | ||
| 1935 | if (obj) { | ||
| 1936 | obj = (lastInstance = obj)[key]; | ||
| 1937 | } | ||
| 1938 | } | ||
| 1939 | if (!bindFnToScope && isFunction(obj)) { | ||
| 1940 | return bind(lastInstance, obj); | ||
| 1941 | } | ||
| 1942 | return obj; | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | /** | ||
| 1946 | * Return the DOM siblings between the first and last node in the given array. | ||
| 1947 | * @param {Array} array like object | ||
| 1948 | * @returns {Array} the inputted object or a jqLite collection containing the nodes | ||
| 1949 | */ | ||
| 1950 | function getBlockNodes(nodes) { | ||
| 1951 | // TODO(perf): update `nodes` instead of creating a new object? | ||
| 1952 | var node = nodes[0]; | ||
| 1953 | var endNode = nodes[nodes.length - 1]; | ||
| 1954 | var blockNodes; | ||
| 1955 | |||
| 1956 | for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { | ||
| 1957 | if (blockNodes || nodes[i] !== node) { | ||
| 1958 | if (!blockNodes) { | ||
| 1959 | blockNodes = jqLite(slice.call(nodes, 0, i)); | ||
| 1960 | } | ||
| 1961 | blockNodes.push(node); | ||
| 1962 | } | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | return blockNodes || nodes; | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | |||
| 1969 | /** | ||
| 1970 | * Creates a new object without a prototype. This object is useful for lookup without having to | ||
| 1971 | * guard against prototypically inherited properties via hasOwnProperty. | ||
| 1972 | * | ||
| 1973 | * Related micro-benchmarks: | ||
| 1974 | * - http://jsperf.com/object-create2 | ||
| 1975 | * - http://jsperf.com/proto-map-lookup/2 | ||
| 1976 | * - http://jsperf.com/for-in-vs-object-keys2 | ||
| 1977 | * | ||
| 1978 | * @returns {Object} | ||
| 1979 | */ | ||
| 1980 | function createMap() { | ||
| 1981 | return Object.create(null); | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | var NODE_TYPE_ELEMENT = 1; | ||
| 1985 | var NODE_TYPE_ATTRIBUTE = 2; | ||
| 1986 | var NODE_TYPE_TEXT = 3; | ||
| 1987 | var NODE_TYPE_COMMENT = 8; | ||
| 1988 | var NODE_TYPE_DOCUMENT = 9; | ||
| 1989 | var NODE_TYPE_DOCUMENT_FRAGMENT = 11; | ||
| 1990 | |||
| 1991 | /** | ||
| 1992 | * @ngdoc type | ||
| 1993 | * @name angular.Module | ||
| 1994 | * @module ng | ||
| 1995 | * @description | ||
| 1996 | * | ||
| 1997 | * Interface for configuring angular {@link angular.module modules}. | ||
| 1998 | */ | ||
| 1999 | |||
| 2000 | function setupModuleLoader(window) { | ||
| 2001 | |||
| 2002 | var $injectorMinErr = minErr('$injector'); | ||
| 2003 | var ngMinErr = minErr('ng'); | ||
| 2004 | |||
| 2005 | function ensure(obj, name, factory) { | ||
| 2006 | return obj[name] || (obj[name] = factory()); | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | var angular = ensure(window, 'angular', Object); | ||
| 2010 | |||
| 2011 | // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap | ||
| 2012 | angular.$$minErr = angular.$$minErr || minErr; | ||
| 2013 | |||
| 2014 | return ensure(angular, 'module', function() { | ||
| 2015 | /** @type {Object.<string, angular.Module>} */ | ||
| 2016 | var modules = {}; | ||
| 2017 | |||
| 2018 | /** | ||
| 2019 | * @ngdoc function | ||
| 2020 | * @name angular.module | ||
| 2021 | * @module ng | ||
| 2022 | * @description | ||
| 2023 | * | ||
| 2024 | * The `angular.module` is a global place for creating, registering and retrieving Angular | ||
| 2025 | * modules. | ||
| 2026 | * All modules (angular core or 3rd party) that should be available to an application must be | ||
| 2027 | * registered using this mechanism. | ||
| 2028 | * | ||
| 2029 | * Passing one argument retrieves an existing {@link angular.Module}, | ||
| 2030 | * whereas passing more than one argument creates a new {@link angular.Module} | ||
| 2031 | * | ||
| 2032 | * | ||
| 2033 | * # Module | ||
| 2034 | * | ||
| 2035 | * A module is a collection of services, directives, controllers, filters, and configuration information. | ||
| 2036 | * `angular.module` is used to configure the {@link auto.$injector $injector}. | ||
| 2037 | * | ||
| 2038 | * ```js | ||
| 2039 | * // Create a new module | ||
| 2040 | * var myModule = angular.module('myModule', []); | ||
| 2041 | * | ||
| 2042 | * // register a new service | ||
| 2043 | * myModule.value('appName', 'MyCoolApp'); | ||
| 2044 | * | ||
| 2045 | * // configure existing services inside initialization blocks. | ||
| 2046 | * myModule.config(['$locationProvider', function($locationProvider) { | ||
| 2047 | * // Configure existing providers | ||
| 2048 | * $locationProvider.hashPrefix('!'); | ||
| 2049 | * }]); | ||
| 2050 | * ``` | ||
| 2051 | * | ||
| 2052 | * Then you can create an injector and load your modules like this: | ||
| 2053 | * | ||
| 2054 | * ```js | ||
| 2055 | * var injector = angular.injector(['ng', 'myModule']) | ||
| 2056 | * ``` | ||
| 2057 | * | ||
| 2058 | * However it's more likely that you'll just use | ||
| 2059 | * {@link ng.directive:ngApp ngApp} or | ||
| 2060 | * {@link angular.bootstrap} to simplify this process for you. | ||
| 2061 | * | ||
| 2062 | * @param {!string} name The name of the module to create or retrieve. | ||
| 2063 | * @param {!Array.<string>=} requires If specified then new module is being created. If | ||
| 2064 | * unspecified then the module is being retrieved for further configuration. | ||
| 2065 | * @param {Function=} configFn Optional configuration function for the module. Same as | ||
| 2066 | * {@link angular.Module#config Module#config()}. | ||
| 2067 | * @returns {angular.Module} new module with the {@link angular.Module} api. | ||
| 2068 | */ | ||
| 2069 | return function module(name, requires, configFn) { | ||
| 2070 | var assertNotHasOwnProperty = function(name, context) { | ||
| 2071 | if (name === 'hasOwnProperty') { | ||
| 2072 | throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); | ||
| 2073 | } | ||
| 2074 | }; | ||
| 2075 | |||
| 2076 | assertNotHasOwnProperty(name, 'module'); | ||
| 2077 | if (requires && modules.hasOwnProperty(name)) { | ||
| 2078 | modules[name] = null; | ||
| 2079 | } | ||
| 2080 | return ensure(modules, name, function() { | ||
| 2081 | if (!requires) { | ||
| 2082 | throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + | ||
| 2083 | "the module name or forgot to load it. If registering a module ensure that you " + | ||
| 2084 | "specify the dependencies as the second argument.", name); | ||
| 2085 | } | ||
| 2086 | |||
| 2087 | /** @type {!Array.<Array.<*>>} */ | ||
| 2088 | var invokeQueue = []; | ||
| 2089 | |||
| 2090 | /** @type {!Array.<Function>} */ | ||
| 2091 | var configBlocks = []; | ||
| 2092 | |||
| 2093 | /** @type {!Array.<Function>} */ | ||
| 2094 | var runBlocks = []; | ||
| 2095 | |||
| 2096 | var config = invokeLater('$injector', 'invoke', 'push', configBlocks); | ||
| 2097 | |||
| 2098 | /** @type {angular.Module} */ | ||
| 2099 | var moduleInstance = { | ||
| 2100 | // Private state | ||
| 2101 | _invokeQueue: invokeQueue, | ||
| 2102 | _configBlocks: configBlocks, | ||
| 2103 | _runBlocks: runBlocks, | ||
| 2104 | |||
| 2105 | /** | ||
| 2106 | * @ngdoc property | ||
| 2107 | * @name angular.Module#requires | ||
| 2108 | * @module ng | ||
| 2109 | * | ||
| 2110 | * @description | ||
| 2111 | * Holds the list of modules which the injector will load before the current module is | ||
| 2112 | * loaded. | ||
| 2113 | */ | ||
| 2114 | requires: requires, | ||
| 2115 | |||
| 2116 | /** | ||
| 2117 | * @ngdoc property | ||
| 2118 | * @name angular.Module#name | ||
| 2119 | * @module ng | ||
| 2120 | * | ||
| 2121 | * @description | ||
| 2122 | * Name of the module. | ||
| 2123 | */ | ||
| 2124 | name: name, | ||
| 2125 | |||
| 2126 | |||
| 2127 | /** | ||
| 2128 | * @ngdoc method | ||
| 2129 | * @name angular.Module#provider | ||
| 2130 | * @module ng | ||
| 2131 | * @param {string} name service name | ||
| 2132 | * @param {Function} providerType Construction function for creating new instance of the | ||
| 2133 | * service. | ||
| 2134 | * @description | ||
| 2135 | * See {@link auto.$provide#provider $provide.provider()}. | ||
| 2136 | */ | ||
| 2137 | provider: invokeLaterAndSetModuleName('$provide', 'provider'), | ||
| 2138 | |||
| 2139 | /** | ||
| 2140 | * @ngdoc method | ||
| 2141 | * @name angular.Module#factory | ||
| 2142 | * @module ng | ||
| 2143 | * @param {string} name service name | ||
| 2144 | * @param {Function} providerFunction Function for creating new instance of the service. | ||
| 2145 | * @description | ||
| 2146 | * See {@link auto.$provide#factory $provide.factory()}. | ||
| 2147 | */ | ||
| 2148 | factory: invokeLaterAndSetModuleName('$provide', 'factory'), | ||
| 2149 | |||
| 2150 | /** | ||
| 2151 | * @ngdoc method | ||
| 2152 | * @name angular.Module#service | ||
| 2153 | * @module ng | ||
| 2154 | * @param {string} name service name | ||
| 2155 | * @param {Function} constructor A constructor function that will be instantiated. | ||
| 2156 | * @description | ||
| 2157 | * See {@link auto.$provide#service $provide.service()}. | ||
| 2158 | */ | ||
| 2159 | service: invokeLaterAndSetModuleName('$provide', 'service'), | ||
| 2160 | |||
| 2161 | /** | ||
| 2162 | * @ngdoc method | ||
| 2163 | * @name angular.Module#value | ||
| 2164 | * @module ng | ||
| 2165 | * @param {string} name service name | ||
| 2166 | * @param {*} object Service instance object. | ||
| 2167 | * @description | ||
| 2168 | * See {@link auto.$provide#value $provide.value()}. | ||
| 2169 | */ | ||
| 2170 | value: invokeLater('$provide', 'value'), | ||
| 2171 | |||
| 2172 | /** | ||
| 2173 | * @ngdoc method | ||
| 2174 | * @name angular.Module#constant | ||
| 2175 | * @module ng | ||
| 2176 | * @param {string} name constant name | ||
| 2177 | * @param {*} object Constant value. | ||
| 2178 | * @description | ||
| 2179 | * Because the constants are fixed, they get applied before other provide methods. | ||
| 2180 | * See {@link auto.$provide#constant $provide.constant()}. | ||
| 2181 | */ | ||
| 2182 | constant: invokeLater('$provide', 'constant', 'unshift'), | ||
| 2183 | |||
| 2184 | /** | ||
| 2185 | * @ngdoc method | ||
| 2186 | * @name angular.Module#decorator | ||
| 2187 | * @module ng | ||
| 2188 | * @param {string} name The name of the service to decorate. | ||
| 2189 | * @param {Function} decorFn This function will be invoked when the service needs to be | ||
| 2190 | * instantiated and should return the decorated service instance. | ||
| 2191 | * @description | ||
| 2192 | * See {@link auto.$provide#decorator $provide.decorator()}. | ||
| 2193 | */ | ||
| 2194 | decorator: invokeLaterAndSetModuleName('$provide', 'decorator'), | ||
| 2195 | |||
| 2196 | /** | ||
| 2197 | * @ngdoc method | ||
| 2198 | * @name angular.Module#animation | ||
| 2199 | * @module ng | ||
| 2200 | * @param {string} name animation name | ||
| 2201 | * @param {Function} animationFactory Factory function for creating new instance of an | ||
| 2202 | * animation. | ||
| 2203 | * @description | ||
| 2204 | * | ||
| 2205 | * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. | ||
| 2206 | * | ||
| 2207 | * | ||
| 2208 | * Defines an animation hook that can be later used with | ||
| 2209 | * {@link $animate $animate} service and directives that use this service. | ||
| 2210 | * | ||
| 2211 | * ```js | ||
| 2212 | * module.animation('.animation-name', function($inject1, $inject2) { | ||
| 2213 | * return { | ||
| 2214 | * eventName : function(element, done) { | ||
| 2215 | * //code to run the animation | ||
| 2216 | * //once complete, then run done() | ||
| 2217 | * return function cancellationFunction(element) { | ||
| 2218 | * //code to cancel the animation | ||
| 2219 | * } | ||
| 2220 | * } | ||
| 2221 | * } | ||
| 2222 | * }) | ||
| 2223 | * ``` | ||
| 2224 | * | ||
| 2225 | * See {@link ng.$animateProvider#register $animateProvider.register()} and | ||
| 2226 | * {@link ngAnimate ngAnimate module} for more information. | ||
| 2227 | */ | ||
| 2228 | animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), | ||
| 2229 | |||
| 2230 | /** | ||
| 2231 | * @ngdoc method | ||
| 2232 | * @name angular.Module#filter | ||
| 2233 | * @module ng | ||
| 2234 | * @param {string} name Filter name - this must be a valid angular expression identifier | ||
| 2235 | * @param {Function} filterFactory Factory function for creating new instance of filter. | ||
| 2236 | * @description | ||
| 2237 | * See {@link ng.$filterProvider#register $filterProvider.register()}. | ||
| 2238 | * | ||
| 2239 | * <div class="alert alert-warning"> | ||
| 2240 | * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. | ||
| 2241 | * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace | ||
| 2242 | * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores | ||
| 2243 | * (`myapp_subsection_filterx`). | ||
| 2244 | * </div> | ||
| 2245 | */ | ||
| 2246 | filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), | ||
| 2247 | |||
| 2248 | /** | ||
| 2249 | * @ngdoc method | ||
| 2250 | * @name angular.Module#controller | ||
| 2251 | * @module ng | ||
| 2252 | * @param {string|Object} name Controller name, or an object map of controllers where the | ||
| 2253 | * keys are the names and the values are the constructors. | ||
| 2254 | * @param {Function} constructor Controller constructor function. | ||
| 2255 | * @description | ||
| 2256 | * See {@link ng.$controllerProvider#register $controllerProvider.register()}. | ||
| 2257 | */ | ||
| 2258 | controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), | ||
| 2259 | |||
| 2260 | /** | ||
| 2261 | * @ngdoc method | ||
| 2262 | * @name angular.Module#directive | ||
| 2263 | * @module ng | ||
| 2264 | * @param {string|Object} name Directive name, or an object map of directives where the | ||
| 2265 | * keys are the names and the values are the factories. | ||
| 2266 | * @param {Function} directiveFactory Factory function for creating new instance of | ||
| 2267 | * directives. | ||
| 2268 | * @description | ||
| 2269 | * See {@link ng.$compileProvider#directive $compileProvider.directive()}. | ||
| 2270 | */ | ||
| 2271 | directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), | ||
| 2272 | |||
| 2273 | /** | ||
| 2274 | * @ngdoc method | ||
| 2275 | * @name angular.Module#component | ||
| 2276 | * @module ng | ||
| 2277 | * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp) | ||
| 2278 | * @param {Object} options Component definition object (a simplified | ||
| 2279 | * {@link ng.$compile#directive-definition-object directive definition object}) | ||
| 2280 | * | ||
| 2281 | * @description | ||
| 2282 | * See {@link ng.$compileProvider#component $compileProvider.component()}. | ||
| 2283 | */ | ||
| 2284 | component: invokeLaterAndSetModuleName('$compileProvider', 'component'), | ||
| 2285 | |||
| 2286 | /** | ||
| 2287 | * @ngdoc method | ||
| 2288 | * @name angular.Module#config | ||
| 2289 | * @module ng | ||
| 2290 | * @param {Function} configFn Execute this function on module load. Useful for service | ||
| 2291 | * configuration. | ||
| 2292 | * @description | ||
| 2293 | * Use this method to register work which needs to be performed on module loading. | ||
| 2294 | * For more about how to configure services, see | ||
| 2295 | * {@link providers#provider-recipe Provider Recipe}. | ||
| 2296 | */ | ||
| 2297 | config: config, | ||
| 2298 | |||
| 2299 | /** | ||
| 2300 | * @ngdoc method | ||
| 2301 | * @name angular.Module#run | ||
| 2302 | * @module ng | ||
| 2303 | * @param {Function} initializationFn Execute this function after injector creation. | ||
| 2304 | * Useful for application initialization. | ||
| 2305 | * @description | ||
| 2306 | * Use this method to register work which should be performed when the injector is done | ||
| 2307 | * loading all modules. | ||
| 2308 | */ | ||
| 2309 | run: function(block) { | ||
| 2310 | runBlocks.push(block); | ||
| 2311 | return this; | ||
| 2312 | } | ||
| 2313 | }; | ||
| 2314 | |||
| 2315 | if (configFn) { | ||
| 2316 | config(configFn); | ||
| 2317 | } | ||
| 2318 | |||
| 2319 | return moduleInstance; | ||
| 2320 | |||
| 2321 | /** | ||
| 2322 | * @param {string} provider | ||
| 2323 | * @param {string} method | ||
| 2324 | * @param {String=} insertMethod | ||
| 2325 | * @returns {angular.Module} | ||
| 2326 | */ | ||
| 2327 | function invokeLater(provider, method, insertMethod, queue) { | ||
| 2328 | if (!queue) queue = invokeQueue; | ||
| 2329 | return function() { | ||
| 2330 | queue[insertMethod || 'push']([provider, method, arguments]); | ||
| 2331 | return moduleInstance; | ||
| 2332 | }; | ||
| 2333 | } | ||
| 2334 | |||
| 2335 | /** | ||
| 2336 | * @param {string} provider | ||
| 2337 | * @param {string} method | ||
| 2338 | * @returns {angular.Module} | ||
| 2339 | */ | ||
| 2340 | function invokeLaterAndSetModuleName(provider, method) { | ||
| 2341 | return function(recipeName, factoryFunction) { | ||
| 2342 | if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; | ||
| 2343 | invokeQueue.push([provider, method, arguments]); | ||
| 2344 | return moduleInstance; | ||
| 2345 | }; | ||
| 2346 | } | ||
| 2347 | }); | ||
| 2348 | }; | ||
| 2349 | }); | ||
| 2350 | |||
| 2351 | } | ||
| 2352 | |||
| 2353 | /* global shallowCopy: true */ | ||
| 2354 | |||
| 2355 | /** | ||
| 2356 | * Creates a shallow copy of an object, an array or a primitive. | ||
| 2357 | * | ||
| 2358 | * Assumes that there are no proto properties for objects. | ||
| 2359 | */ | ||
| 2360 | function shallowCopy(src, dst) { | ||
| 2361 | if (isArray(src)) { | ||
| 2362 | dst = dst || []; | ||
| 2363 | |||
| 2364 | for (var i = 0, ii = src.length; i < ii; i++) { | ||
| 2365 | dst[i] = src[i]; | ||
| 2366 | } | ||
| 2367 | } else if (isObject(src)) { | ||
| 2368 | dst = dst || {}; | ||
| 2369 | |||
| 2370 | for (var key in src) { | ||
| 2371 | if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { | ||
| 2372 | dst[key] = src[key]; | ||
| 2373 | } | ||
| 2374 | } | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | return dst || src; | ||
| 2378 | } | ||
| 2379 | |||
| 2380 | /* global toDebugString: true */ | ||
| 2381 | |||
| 2382 | function serializeObject(obj) { | ||
| 2383 | var seen = []; | ||
| 2384 | |||
| 2385 | return JSON.stringify(obj, function(key, val) { | ||
| 2386 | val = toJsonReplacer(key, val); | ||
| 2387 | if (isObject(val)) { | ||
| 2388 | |||
| 2389 | if (seen.indexOf(val) >= 0) return '...'; | ||
| 2390 | |||
| 2391 | seen.push(val); | ||
| 2392 | } | ||
| 2393 | return val; | ||
| 2394 | }); | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | function toDebugString(obj) { | ||
| 2398 | if (typeof obj === 'function') { | ||
| 2399 | return obj.toString().replace(/ \{[\s\S]*$/, ''); | ||
| 2400 | } else if (isUndefined(obj)) { | ||
| 2401 | return 'undefined'; | ||
| 2402 | } else if (typeof obj !== 'string') { | ||
| 2403 | return serializeObject(obj); | ||
| 2404 | } | ||
| 2405 | return obj; | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | /* global angularModule: true, | ||
| 2409 | version: true, | ||
| 2410 | |||
| 2411 | $CompileProvider, | ||
| 2412 | |||
| 2413 | htmlAnchorDirective, | ||
| 2414 | inputDirective, | ||
| 2415 | inputDirective, | ||
| 2416 | formDirective, | ||
| 2417 | scriptDirective, | ||
| 2418 | selectDirective, | ||
| 2419 | styleDirective, | ||
| 2420 | optionDirective, | ||
| 2421 | ngBindDirective, | ||
| 2422 | ngBindHtmlDirective, | ||
| 2423 | ngBindTemplateDirective, | ||
| 2424 | ngClassDirective, | ||
| 2425 | ngClassEvenDirective, | ||
| 2426 | ngClassOddDirective, | ||
| 2427 | ngCloakDirective, | ||
| 2428 | ngControllerDirective, | ||
| 2429 | ngFormDirective, | ||
| 2430 | ngHideDirective, | ||
| 2431 | ngIfDirective, | ||
| 2432 | ngIncludeDirective, | ||
| 2433 | ngIncludeFillContentDirective, | ||
| 2434 | ngInitDirective, | ||
| 2435 | ngNonBindableDirective, | ||
| 2436 | ngPluralizeDirective, | ||
| 2437 | ngRepeatDirective, | ||
| 2438 | ngShowDirective, | ||
| 2439 | ngStyleDirective, | ||
| 2440 | ngSwitchDirective, | ||
| 2441 | ngSwitchWhenDirective, | ||
| 2442 | ngSwitchDefaultDirective, | ||
| 2443 | ngOptionsDirective, | ||
| 2444 | ngTranscludeDirective, | ||
| 2445 | ngModelDirective, | ||
| 2446 | ngListDirective, | ||
| 2447 | ngChangeDirective, | ||
| 2448 | patternDirective, | ||
| 2449 | patternDirective, | ||
| 2450 | requiredDirective, | ||
| 2451 | requiredDirective, | ||
| 2452 | minlengthDirective, | ||
| 2453 | minlengthDirective, | ||
| 2454 | maxlengthDirective, | ||
| 2455 | maxlengthDirective, | ||
| 2456 | ngValueDirective, | ||
| 2457 | ngModelOptionsDirective, | ||
| 2458 | ngAttributeAliasDirectives, | ||
| 2459 | ngEventDirectives, | ||
| 2460 | |||
| 2461 | $AnchorScrollProvider, | ||
| 2462 | $AnimateProvider, | ||
| 2463 | $CoreAnimateCssProvider, | ||
| 2464 | $$CoreAnimateJsProvider, | ||
| 2465 | $$CoreAnimateQueueProvider, | ||
| 2466 | $$AnimateRunnerFactoryProvider, | ||
| 2467 | $$AnimateAsyncRunFactoryProvider, | ||
| 2468 | $BrowserProvider, | ||
| 2469 | $CacheFactoryProvider, | ||
| 2470 | $ControllerProvider, | ||
| 2471 | $DateProvider, | ||
| 2472 | $DocumentProvider, | ||
| 2473 | $ExceptionHandlerProvider, | ||
| 2474 | $FilterProvider, | ||
| 2475 | $$ForceReflowProvider, | ||
| 2476 | $InterpolateProvider, | ||
| 2477 | $IntervalProvider, | ||
| 2478 | $$HashMapProvider, | ||
| 2479 | $HttpProvider, | ||
| 2480 | $HttpParamSerializerProvider, | ||
| 2481 | $HttpParamSerializerJQLikeProvider, | ||
| 2482 | $HttpBackendProvider, | ||
| 2483 | $xhrFactoryProvider, | ||
| 2484 | $jsonpCallbacksProvider, | ||
| 2485 | $LocationProvider, | ||
| 2486 | $LogProvider, | ||
| 2487 | $ParseProvider, | ||
| 2488 | $RootScopeProvider, | ||
| 2489 | $QProvider, | ||
| 2490 | $$QProvider, | ||
| 2491 | $$SanitizeUriProvider, | ||
| 2492 | $SceProvider, | ||
| 2493 | $SceDelegateProvider, | ||
| 2494 | $SnifferProvider, | ||
| 2495 | $TemplateCacheProvider, | ||
| 2496 | $TemplateRequestProvider, | ||
| 2497 | $$TestabilityProvider, | ||
| 2498 | $TimeoutProvider, | ||
| 2499 | $$RAFProvider, | ||
| 2500 | $WindowProvider, | ||
| 2501 | $$jqLiteProvider, | ||
| 2502 | $$CookieReaderProvider | ||
| 2503 | */ | ||
| 2504 | |||
| 2505 | |||
| 2506 | /** | ||
| 2507 | * @ngdoc object | ||
| 2508 | * @name angular.version | ||
| 2509 | * @module ng | ||
| 2510 | * @description | ||
| 2511 | * An object that contains information about the current AngularJS version. | ||
| 2512 | * | ||
| 2513 | * This object has the following properties: | ||
| 2514 | * | ||
| 2515 | * - `full` – `{string}` – Full version string, such as "0.9.18". | ||
| 2516 | * - `major` – `{number}` – Major version number, such as "0". | ||
| 2517 | * - `minor` – `{number}` – Minor version number, such as "9". | ||
| 2518 | * - `dot` – `{number}` – Dot version number, such as "18". | ||
| 2519 | * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". | ||
| 2520 | */ | ||
| 2521 | var version = { | ||
| 2522 | full: '1.5.8', // all of these placeholder strings will be replaced by grunt's | ||
| 2523 | major: 1, // package task | ||
| 2524 | minor: 5, | ||
| 2525 | dot: 8, | ||
| 2526 | codeName: 'arbitrary-fallbacks' | ||
| 2527 | }; | ||
| 2528 | |||
| 2529 | |||
| 2530 | function publishExternalAPI(angular) { | ||
| 2531 | extend(angular, { | ||
| 2532 | 'bootstrap': bootstrap, | ||
| 2533 | 'copy': copy, | ||
| 2534 | 'extend': extend, | ||
| 2535 | 'merge': merge, | ||
| 2536 | 'equals': equals, | ||
| 2537 | 'element': jqLite, | ||
| 2538 | 'forEach': forEach, | ||
| 2539 | 'injector': createInjector, | ||
| 2540 | 'noop': noop, | ||
| 2541 | 'bind': bind, | ||
| 2542 | 'toJson': toJson, | ||
| 2543 | 'fromJson': fromJson, | ||
| 2544 | 'identity': identity, | ||
| 2545 | 'isUndefined': isUndefined, | ||
| 2546 | 'isDefined': isDefined, | ||
| 2547 | 'isString': isString, | ||
| 2548 | 'isFunction': isFunction, | ||
| 2549 | 'isObject': isObject, | ||
| 2550 | 'isNumber': isNumber, | ||
| 2551 | 'isElement': isElement, | ||
| 2552 | 'isArray': isArray, | ||
| 2553 | 'version': version, | ||
| 2554 | 'isDate': isDate, | ||
| 2555 | 'lowercase': lowercase, | ||
| 2556 | 'uppercase': uppercase, | ||
| 2557 | 'callbacks': {$$counter: 0}, | ||
| 2558 | 'getTestability': getTestability, | ||
| 2559 | '$$minErr': minErr, | ||
| 2560 | '$$csp': csp, | ||
| 2561 | 'reloadWithDebugInfo': reloadWithDebugInfo | ||
| 2562 | }); | ||
| 2563 | |||
| 2564 | angularModule = setupModuleLoader(window); | ||
| 2565 | |||
| 2566 | angularModule('ng', ['ngLocale'], ['$provide', | ||
| 2567 | function ngModule($provide) { | ||
| 2568 | // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. | ||
| 2569 | $provide.provider({ | ||
| 2570 | $$sanitizeUri: $$SanitizeUriProvider | ||
| 2571 | }); | ||
| 2572 | $provide.provider('$compile', $CompileProvider). | ||
| 2573 | directive({ | ||
| 2574 | a: htmlAnchorDirective, | ||
| 2575 | input: inputDirective, | ||
| 2576 | textarea: inputDirective, | ||
| 2577 | form: formDirective, | ||
| 2578 | script: scriptDirective, | ||
| 2579 | select: selectDirective, | ||
| 2580 | style: styleDirective, | ||
| 2581 | option: optionDirective, | ||
| 2582 | ngBind: ngBindDirective, | ||
| 2583 | ngBindHtml: ngBindHtmlDirective, | ||
| 2584 | ngBindTemplate: ngBindTemplateDirective, | ||
| 2585 | ngClass: ngClassDirective, | ||
| 2586 | ngClassEven: ngClassEvenDirective, | ||
| 2587 | ngClassOdd: ngClassOddDirective, | ||
| 2588 | ngCloak: ngCloakDirective, | ||
| 2589 | ngController: ngControllerDirective, | ||
| 2590 | ngForm: ngFormDirective, | ||
| 2591 | ngHide: ngHideDirective, | ||
| 2592 | ngIf: ngIfDirective, | ||
| 2593 | ngInclude: ngIncludeDirective, | ||
| 2594 | ngInit: ngInitDirective, | ||
| 2595 | ngNonBindable: ngNonBindableDirective, | ||
| 2596 | ngPluralize: ngPluralizeDirective, | ||
| 2597 | ngRepeat: ngRepeatDirective, | ||
| 2598 | ngShow: ngShowDirective, | ||
| 2599 | ngStyle: ngStyleDirective, | ||
| 2600 | ngSwitch: ngSwitchDirective, | ||
| 2601 | ngSwitchWhen: ngSwitchWhenDirective, | ||
| 2602 | ngSwitchDefault: ngSwitchDefaultDirective, | ||
| 2603 | ngOptions: ngOptionsDirective, | ||
| 2604 | ngTransclude: ngTranscludeDirective, | ||
| 2605 | ngModel: ngModelDirective, | ||
| 2606 | ngList: ngListDirective, | ||
| 2607 | ngChange: ngChangeDirective, | ||
| 2608 | pattern: patternDirective, | ||
| 2609 | ngPattern: patternDirective, | ||
| 2610 | required: requiredDirective, | ||
| 2611 | ngRequired: requiredDirective, | ||
| 2612 | minlength: minlengthDirective, | ||
| 2613 | ngMinlength: minlengthDirective, | ||
| 2614 | maxlength: maxlengthDirective, | ||
| 2615 | ngMaxlength: maxlengthDirective, | ||
| 2616 | ngValue: ngValueDirective, | ||
| 2617 | ngModelOptions: ngModelOptionsDirective | ||
| 2618 | }). | ||
| 2619 | directive({ | ||
| 2620 | ngInclude: ngIncludeFillContentDirective | ||
| 2621 | }). | ||
| 2622 | directive(ngAttributeAliasDirectives). | ||
| 2623 | directive(ngEventDirectives); | ||
| 2624 | $provide.provider({ | ||
| 2625 | $anchorScroll: $AnchorScrollProvider, | ||
| 2626 | $animate: $AnimateProvider, | ||
| 2627 | $animateCss: $CoreAnimateCssProvider, | ||
| 2628 | $$animateJs: $$CoreAnimateJsProvider, | ||
| 2629 | $$animateQueue: $$CoreAnimateQueueProvider, | ||
| 2630 | $$AnimateRunner: $$AnimateRunnerFactoryProvider, | ||
| 2631 | $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider, | ||
| 2632 | $browser: $BrowserProvider, | ||
| 2633 | $cacheFactory: $CacheFactoryProvider, | ||
| 2634 | $controller: $ControllerProvider, | ||
| 2635 | $document: $DocumentProvider, | ||
| 2636 | $exceptionHandler: $ExceptionHandlerProvider, | ||
| 2637 | $filter: $FilterProvider, | ||
| 2638 | $$forceReflow: $$ForceReflowProvider, | ||
| 2639 | $interpolate: $InterpolateProvider, | ||
| 2640 | $interval: $IntervalProvider, | ||
| 2641 | $http: $HttpProvider, | ||
| 2642 | $httpParamSerializer: $HttpParamSerializerProvider, | ||
| 2643 | $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, | ||
| 2644 | $httpBackend: $HttpBackendProvider, | ||
| 2645 | $xhrFactory: $xhrFactoryProvider, | ||
| 2646 | $jsonpCallbacks: $jsonpCallbacksProvider, | ||
| 2647 | $location: $LocationProvider, | ||
| 2648 | $log: $LogProvider, | ||
| 2649 | $parse: $ParseProvider, | ||
| 2650 | $rootScope: $RootScopeProvider, | ||
| 2651 | $q: $QProvider, | ||
| 2652 | $$q: $$QProvider, | ||
| 2653 | $sce: $SceProvider, | ||
| 2654 | $sceDelegate: $SceDelegateProvider, | ||
| 2655 | $sniffer: $SnifferProvider, | ||
| 2656 | $templateCache: $TemplateCacheProvider, | ||
| 2657 | $templateRequest: $TemplateRequestProvider, | ||
| 2658 | $$testability: $$TestabilityProvider, | ||
| 2659 | $timeout: $TimeoutProvider, | ||
| 2660 | $window: $WindowProvider, | ||
| 2661 | $$rAF: $$RAFProvider, | ||
| 2662 | $$jqLite: $$jqLiteProvider, | ||
| 2663 | $$HashMap: $$HashMapProvider, | ||
| 2664 | $$cookieReader: $$CookieReaderProvider | ||
| 2665 | }); | ||
| 2666 | } | ||
| 2667 | ]); | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 2671 | * Any commits to this file should be reviewed with security in mind. * | ||
| 2672 | * Changes to this file can potentially create security vulnerabilities. * | ||
| 2673 | * An approval from 2 Core members with history of modifying * | ||
| 2674 | * this file is required. * | ||
| 2675 | * * | ||
| 2676 | * Does the change somehow allow for arbitrary javascript to be executed? * | ||
| 2677 | * Or allows for someone to change the prototype of built-in objects? * | ||
| 2678 | * Or gives undesired access to variables likes document or window? * | ||
| 2679 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | ||
| 2680 | |||
| 2681 | /* global JQLitePrototype: true, | ||
| 2682 | addEventListenerFn: true, | ||
| 2683 | removeEventListenerFn: true, | ||
| 2684 | BOOLEAN_ATTR: true, | ||
| 2685 | ALIASED_ATTR: true, | ||
| 2686 | */ | ||
| 2687 | |||
| 2688 | ////////////////////////////////// | ||
| 2689 | //JQLite | ||
| 2690 | ////////////////////////////////// | ||
| 2691 | |||
| 2692 | /** | ||
| 2693 | * @ngdoc function | ||
| 2694 | * @name angular.element | ||
| 2695 | * @module ng | ||
| 2696 | * @kind function | ||
| 2697 | * | ||
| 2698 | * @description | ||
| 2699 | * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. | ||
| 2700 | * | ||
| 2701 | * If jQuery is available, `angular.element` is an alias for the | ||
| 2702 | * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` | ||
| 2703 | * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**. | ||
| 2704 | * | ||
| 2705 | * jqLite is a tiny, API-compatible subset of jQuery that allows | ||
| 2706 | * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most | ||
| 2707 | * commonly needed functionality with the goal of having a very small footprint. | ||
| 2708 | * | ||
| 2709 | * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the | ||
| 2710 | * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a | ||
| 2711 | * specific version of jQuery if multiple versions exist on the page. | ||
| 2712 | * | ||
| 2713 | * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or | ||
| 2714 | * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div> | ||
| 2715 | * | ||
| 2716 | * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements | ||
| 2717 | * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)` | ||
| 2718 | * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div> | ||
| 2719 | * | ||
| 2720 | * ## Angular's jqLite | ||
| 2721 | * jqLite provides only the following jQuery methods: | ||
| 2722 | * | ||
| 2723 | * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument | ||
| 2724 | * - [`after()`](http://api.jquery.com/after/) | ||
| 2725 | * - [`append()`](http://api.jquery.com/append/) | ||
| 2726 | * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters | ||
| 2727 | * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData | ||
| 2728 | * - [`children()`](http://api.jquery.com/children/) - Does not support selectors | ||
| 2729 | * - [`clone()`](http://api.jquery.com/clone/) | ||
| 2730 | * - [`contents()`](http://api.jquery.com/contents/) | ||
| 2731 | * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. | ||
| 2732 | * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing. | ||
| 2733 | * - [`data()`](http://api.jquery.com/data/) | ||
| 2734 | * - [`detach()`](http://api.jquery.com/detach/) | ||
| 2735 | * - [`empty()`](http://api.jquery.com/empty/) | ||
| 2736 | * - [`eq()`](http://api.jquery.com/eq/) | ||
| 2737 | * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name | ||
| 2738 | * - [`hasClass()`](http://api.jquery.com/hasClass/) | ||
| 2739 | * - [`html()`](http://api.jquery.com/html/) | ||
| 2740 | * - [`next()`](http://api.jquery.com/next/) - Does not support selectors | ||
| 2741 | * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData | ||
| 2742 | * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter | ||
| 2743 | * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors | ||
| 2744 | * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors | ||
| 2745 | * - [`prepend()`](http://api.jquery.com/prepend/) | ||
| 2746 | * - [`prop()`](http://api.jquery.com/prop/) | ||
| 2747 | * - [`ready()`](http://api.jquery.com/ready/) | ||
| 2748 | * - [`remove()`](http://api.jquery.com/remove/) | ||
| 2749 | * - [`removeAttr()`](http://api.jquery.com/removeAttr/) | ||
| 2750 | * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument | ||
| 2751 | * - [`removeData()`](http://api.jquery.com/removeData/) | ||
| 2752 | * - [`replaceWith()`](http://api.jquery.com/replaceWith/) | ||
| 2753 | * - [`text()`](http://api.jquery.com/text/) | ||
| 2754 | * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument | ||
| 2755 | * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers | ||
| 2756 | * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter | ||
| 2757 | * - [`val()`](http://api.jquery.com/val/) | ||
| 2758 | * - [`wrap()`](http://api.jquery.com/wrap/) | ||
| 2759 | * | ||
| 2760 | * ## jQuery/jqLite Extras | ||
| 2761 | * Angular also provides the following additional methods and events to both jQuery and jqLite: | ||
| 2762 | * | ||
| 2763 | * ### Events | ||
| 2764 | * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event | ||
| 2765 | * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM | ||
| 2766 | * element before it is removed. | ||
| 2767 | * | ||
| 2768 | * ### Methods | ||
| 2769 | * - `controller(name)` - retrieves the controller of the current element or its parent. By default | ||
| 2770 | * retrieves controller associated with the `ngController` directive. If `name` is provided as | ||
| 2771 | * camelCase directive name, then the controller for this directive will be retrieved (e.g. | ||
| 2772 | * `'ngModel'`). | ||
| 2773 | * - `injector()` - retrieves the injector of the current element or its parent. | ||
| 2774 | * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current | ||
| 2775 | * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to | ||
| 2776 | * be enabled. | ||
| 2777 | * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the | ||
| 2778 | * current element. This getter should be used only on elements that contain a directive which starts a new isolate | ||
| 2779 | * scope. Calling `scope()` on this element always returns the original non-isolate scope. | ||
| 2780 | * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. | ||
| 2781 | * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top | ||
| 2782 | * parent element is reached. | ||
| 2783 | * | ||
| 2784 | * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See | ||
| 2785 | * https://github.com/angular/angular.js/issues/14251 for more information. | ||
| 2786 | * | ||
| 2787 | * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. | ||
| 2788 | * @returns {Object} jQuery object. | ||
| 2789 | */ | ||
| 2790 | |||
| 2791 | JQLite.expando = 'ng339'; | ||
| 2792 | |||
| 2793 | var jqCache = JQLite.cache = {}, | ||
| 2794 | jqId = 1, | ||
| 2795 | addEventListenerFn = function(element, type, fn) { | ||
| 2796 | element.addEventListener(type, fn, false); | ||
| 2797 | }, | ||
| 2798 | removeEventListenerFn = function(element, type, fn) { | ||
| 2799 | element.removeEventListener(type, fn, false); | ||
| 2800 | }; | ||
| 2801 | |||
| 2802 | /* | ||
| 2803 | * !!! This is an undocumented "private" function !!! | ||
| 2804 | */ | ||
| 2805 | JQLite._data = function(node) { | ||
| 2806 | //jQuery always returns an object on cache miss | ||
| 2807 | return this.cache[node[this.expando]] || {}; | ||
| 2808 | }; | ||
| 2809 | |||
| 2810 | function jqNextId() { return ++jqId; } | ||
| 2811 | |||
| 2812 | |||
| 2813 | var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; | ||
| 2814 | var MOZ_HACK_REGEXP = /^moz([A-Z])/; | ||
| 2815 | var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; | ||
| 2816 | var jqLiteMinErr = minErr('jqLite'); | ||
| 2817 | |||
| 2818 | /** | ||
| 2819 | * Converts snake_case to camelCase. | ||
| 2820 | * Also there is special case for Moz prefix starting with upper case letter. | ||
| 2821 | * @param name Name to normalize | ||
| 2822 | */ | ||
| 2823 | function camelCase(name) { | ||
| 2824 | return name. | ||
| 2825 | replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { | ||
| 2826 | return offset ? letter.toUpperCase() : letter; | ||
| 2827 | }). | ||
| 2828 | replace(MOZ_HACK_REGEXP, 'Moz$1'); | ||
| 2829 | } | ||
| 2830 | |||
| 2831 | var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/; | ||
| 2832 | var HTML_REGEXP = /<|&#?\w+;/; | ||
| 2833 | var TAG_NAME_REGEXP = /<([\w:-]+)/; | ||
| 2834 | var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi; | ||
| 2835 | |||
| 2836 | var wrapMap = { | ||
| 2837 | 'option': [1, '<select multiple="multiple">', '</select>'], | ||
| 2838 | |||
| 2839 | 'thead': [1, '<table>', '</table>'], | ||
| 2840 | 'col': [2, '<table><colgroup>', '</colgroup></table>'], | ||
| 2841 | 'tr': [2, '<table><tbody>', '</tbody></table>'], | ||
| 2842 | 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], | ||
| 2843 | '_default': [0, "", ""] | ||
| 2844 | }; | ||
| 2845 | |||
| 2846 | wrapMap.optgroup = wrapMap.option; | ||
| 2847 | wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; | ||
| 2848 | wrapMap.th = wrapMap.td; | ||
| 2849 | |||
| 2850 | |||
| 2851 | function jqLiteIsTextNode(html) { | ||
| 2852 | return !HTML_REGEXP.test(html); | ||
| 2853 | } | ||
| 2854 | |||
| 2855 | function jqLiteAcceptsData(node) { | ||
| 2856 | // The window object can accept data but has no nodeType | ||
| 2857 | // Otherwise we are only interested in elements (1) and documents (9) | ||
| 2858 | var nodeType = node.nodeType; | ||
| 2859 | return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | function jqLiteHasData(node) { | ||
| 2863 | for (var key in jqCache[node.ng339]) { | ||
| 2864 | return true; | ||
| 2865 | } | ||
| 2866 | return false; | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | function jqLiteCleanData(nodes) { | ||
| 2870 | for (var i = 0, ii = nodes.length; i < ii; i++) { | ||
| 2871 | jqLiteRemoveData(nodes[i]); | ||
| 2872 | } | ||
| 2873 | } | ||
| 2874 | |||
| 2875 | function jqLiteBuildFragment(html, context) { | ||
| 2876 | var tmp, tag, wrap, | ||
| 2877 | fragment = context.createDocumentFragment(), | ||
| 2878 | nodes = [], i; | ||
| 2879 | |||
| 2880 | if (jqLiteIsTextNode(html)) { | ||
| 2881 | // Convert non-html into a text node | ||
| 2882 | nodes.push(context.createTextNode(html)); | ||
| 2883 | } else { | ||
| 2884 | // Convert html into DOM nodes | ||
| 2885 | tmp = fragment.appendChild(context.createElement("div")); | ||
| 2886 | tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); | ||
| 2887 | wrap = wrapMap[tag] || wrapMap._default; | ||
| 2888 | tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2]; | ||
| 2889 | |||
| 2890 | // Descend through wrappers to the right content | ||
| 2891 | i = wrap[0]; | ||
| 2892 | while (i--) { | ||
| 2893 | tmp = tmp.lastChild; | ||
| 2894 | } | ||
| 2895 | |||
| 2896 | nodes = concat(nodes, tmp.childNodes); | ||
| 2897 | |||
| 2898 | tmp = fragment.firstChild; | ||
| 2899 | tmp.textContent = ""; | ||
| 2900 | } | ||
| 2901 | |||
| 2902 | // Remove wrapper from fragment | ||
| 2903 | fragment.textContent = ""; | ||
| 2904 | fragment.innerHTML = ""; // Clear inner HTML | ||
| 2905 | forEach(nodes, function(node) { | ||
| 2906 | fragment.appendChild(node); | ||
| 2907 | }); | ||
| 2908 | |||
| 2909 | return fragment; | ||
| 2910 | } | ||
| 2911 | |||
| 2912 | function jqLiteParseHTML(html, context) { | ||
| 2913 | context = context || window.document; | ||
| 2914 | var parsed; | ||
| 2915 | |||
| 2916 | if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { | ||
| 2917 | return [context.createElement(parsed[1])]; | ||
| 2918 | } | ||
| 2919 | |||
| 2920 | if ((parsed = jqLiteBuildFragment(html, context))) { | ||
| 2921 | return parsed.childNodes; | ||
| 2922 | } | ||
| 2923 | |||
| 2924 | return []; | ||
| 2925 | } | ||
| 2926 | |||
| 2927 | function jqLiteWrapNode(node, wrapper) { | ||
| 2928 | var parent = node.parentNode; | ||
| 2929 | |||
| 2930 | if (parent) { | ||
| 2931 | parent.replaceChild(wrapper, node); | ||
| 2932 | } | ||
| 2933 | |||
| 2934 | wrapper.appendChild(node); | ||
| 2935 | } | ||
| 2936 | |||
| 2937 | |||
| 2938 | // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. | ||
| 2939 | var jqLiteContains = window.Node.prototype.contains || function(arg) { | ||
| 2940 | // jshint bitwise: false | ||
| 2941 | return !!(this.compareDocumentPosition(arg) & 16); | ||
| 2942 | // jshint bitwise: true | ||
| 2943 | }; | ||
| 2944 | |||
| 2945 | ///////////////////////////////////////////// | ||
| 2946 | function JQLite(element) { | ||
| 2947 | if (element instanceof JQLite) { | ||
| 2948 | return element; | ||
| 2949 | } | ||
| 2950 | |||
| 2951 | var argIsString; | ||
| 2952 | |||
| 2953 | if (isString(element)) { | ||
| 2954 | element = trim(element); | ||
| 2955 | argIsString = true; | ||
| 2956 | } | ||
| 2957 | if (!(this instanceof JQLite)) { | ||
| 2958 | if (argIsString && element.charAt(0) != '<') { | ||
| 2959 | throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); | ||
| 2960 | } | ||
| 2961 | return new JQLite(element); | ||
| 2962 | } | ||
| 2963 | |||
| 2964 | if (argIsString) { | ||
| 2965 | jqLiteAddNodes(this, jqLiteParseHTML(element)); | ||
| 2966 | } else { | ||
| 2967 | jqLiteAddNodes(this, element); | ||
| 2968 | } | ||
| 2969 | } | ||
| 2970 | |||
| 2971 | function jqLiteClone(element) { | ||
| 2972 | return element.cloneNode(true); | ||
| 2973 | } | ||
| 2974 | |||
| 2975 | function jqLiteDealoc(element, onlyDescendants) { | ||
| 2976 | if (!onlyDescendants) jqLiteRemoveData(element); | ||
| 2977 | |||
| 2978 | if (element.querySelectorAll) { | ||
| 2979 | var descendants = element.querySelectorAll('*'); | ||
| 2980 | for (var i = 0, l = descendants.length; i < l; i++) { | ||
| 2981 | jqLiteRemoveData(descendants[i]); | ||
| 2982 | } | ||
| 2983 | } | ||
| 2984 | } | ||
| 2985 | |||
| 2986 | function jqLiteOff(element, type, fn, unsupported) { | ||
| 2987 | if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); | ||
| 2988 | |||
| 2989 | var expandoStore = jqLiteExpandoStore(element); | ||
| 2990 | var events = expandoStore && expandoStore.events; | ||
| 2991 | var handle = expandoStore && expandoStore.handle; | ||
| 2992 | |||
| 2993 | if (!handle) return; //no listeners registered | ||
| 2994 | |||
| 2995 | if (!type) { | ||
| 2996 | for (type in events) { | ||
| 2997 | if (type !== '$destroy') { | ||
| 2998 | removeEventListenerFn(element, type, handle); | ||
| 2999 | } | ||
| 3000 | delete events[type]; | ||
| 3001 | } | ||
| 3002 | } else { | ||
| 3003 | |||
| 3004 | var removeHandler = function(type) { | ||
| 3005 | var listenerFns = events[type]; | ||
| 3006 | if (isDefined(fn)) { | ||
| 3007 | arrayRemove(listenerFns || [], fn); | ||
| 3008 | } | ||
| 3009 | if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { | ||
| 3010 | removeEventListenerFn(element, type, handle); | ||
| 3011 | delete events[type]; | ||
| 3012 | } | ||
| 3013 | }; | ||
| 3014 | |||
| 3015 | forEach(type.split(' '), function(type) { | ||
| 3016 | removeHandler(type); | ||
| 3017 | if (MOUSE_EVENT_MAP[type]) { | ||
| 3018 | removeHandler(MOUSE_EVENT_MAP[type]); | ||
| 3019 | } | ||
| 3020 | }); | ||
| 3021 | } | ||
| 3022 | } | ||
| 3023 | |||
| 3024 | function jqLiteRemoveData(element, name) { | ||
| 3025 | var expandoId = element.ng339; | ||
| 3026 | var expandoStore = expandoId && jqCache[expandoId]; | ||
| 3027 | |||
| 3028 | if (expandoStore) { | ||
| 3029 | if (name) { | ||
| 3030 | delete expandoStore.data[name]; | ||
| 3031 | return; | ||
| 3032 | } | ||
| 3033 | |||
| 3034 | if (expandoStore.handle) { | ||
| 3035 | if (expandoStore.events.$destroy) { | ||
| 3036 | expandoStore.handle({}, '$destroy'); | ||
| 3037 | } | ||
| 3038 | jqLiteOff(element); | ||
| 3039 | } | ||
| 3040 | delete jqCache[expandoId]; | ||
| 3041 | element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it | ||
| 3042 | } | ||
| 3043 | } | ||
| 3044 | |||
| 3045 | |||
| 3046 | function jqLiteExpandoStore(element, createIfNecessary) { | ||
| 3047 | var expandoId = element.ng339, | ||
| 3048 | expandoStore = expandoId && jqCache[expandoId]; | ||
| 3049 | |||
| 3050 | if (createIfNecessary && !expandoStore) { | ||
| 3051 | element.ng339 = expandoId = jqNextId(); | ||
| 3052 | expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; | ||
| 3053 | } | ||
| 3054 | |||
| 3055 | return expandoStore; | ||
| 3056 | } | ||
| 3057 | |||
| 3058 | |||
| 3059 | function jqLiteData(element, key, value) { | ||
| 3060 | if (jqLiteAcceptsData(element)) { | ||
| 3061 | |||
| 3062 | var isSimpleSetter = isDefined(value); | ||
| 3063 | var isSimpleGetter = !isSimpleSetter && key && !isObject(key); | ||
| 3064 | var massGetter = !key; | ||
| 3065 | var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); | ||
| 3066 | var data = expandoStore && expandoStore.data; | ||
| 3067 | |||
| 3068 | if (isSimpleSetter) { // data('key', value) | ||
| 3069 | data[key] = value; | ||
| 3070 | } else { | ||
| 3071 | if (massGetter) { // data() | ||
| 3072 | return data; | ||
| 3073 | } else { | ||
| 3074 | if (isSimpleGetter) { // data('key') | ||
| 3075 | // don't force creation of expandoStore if it doesn't exist yet | ||
| 3076 | return data && data[key]; | ||
| 3077 | } else { // mass-setter: data({key1: val1, key2: val2}) | ||
| 3078 | extend(data, key); | ||
| 3079 | } | ||
| 3080 | } | ||
| 3081 | } | ||
| 3082 | } | ||
| 3083 | } | ||
| 3084 | |||
| 3085 | function jqLiteHasClass(element, selector) { | ||
| 3086 | if (!element.getAttribute) return false; | ||
| 3087 | return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). | ||
| 3088 | indexOf(" " + selector + " ") > -1); | ||
| 3089 | } | ||
| 3090 | |||
| 3091 | function jqLiteRemoveClass(element, cssClasses) { | ||
| 3092 | if (cssClasses && element.setAttribute) { | ||
| 3093 | forEach(cssClasses.split(' '), function(cssClass) { | ||
| 3094 | element.setAttribute('class', trim( | ||
| 3095 | (" " + (element.getAttribute('class') || '') + " ") | ||
| 3096 | .replace(/[\n\t]/g, " ") | ||
| 3097 | .replace(" " + trim(cssClass) + " ", " ")) | ||
| 3098 | ); | ||
| 3099 | }); | ||
| 3100 | } | ||
| 3101 | } | ||
| 3102 | |||
| 3103 | function jqLiteAddClass(element, cssClasses) { | ||
| 3104 | if (cssClasses && element.setAttribute) { | ||
| 3105 | var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') | ||
| 3106 | .replace(/[\n\t]/g, " "); | ||
| 3107 | |||
| 3108 | forEach(cssClasses.split(' '), function(cssClass) { | ||
| 3109 | cssClass = trim(cssClass); | ||
| 3110 | if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { | ||
| 3111 | existingClasses += cssClass + ' '; | ||
| 3112 | } | ||
| 3113 | }); | ||
| 3114 | |||
| 3115 | element.setAttribute('class', trim(existingClasses)); | ||
| 3116 | } | ||
| 3117 | } | ||
| 3118 | |||
| 3119 | |||
| 3120 | function jqLiteAddNodes(root, elements) { | ||
| 3121 | // THIS CODE IS VERY HOT. Don't make changes without benchmarking. | ||
| 3122 | |||
| 3123 | if (elements) { | ||
| 3124 | |||
| 3125 | // if a Node (the most common case) | ||
| 3126 | if (elements.nodeType) { | ||
| 3127 | root[root.length++] = elements; | ||
| 3128 | } else { | ||
| 3129 | var length = elements.length; | ||
| 3130 | |||
| 3131 | // if an Array or NodeList and not a Window | ||
| 3132 | if (typeof length === 'number' && elements.window !== elements) { | ||
| 3133 | if (length) { | ||
| 3134 | for (var i = 0; i < length; i++) { | ||
| 3135 | root[root.length++] = elements[i]; | ||
| 3136 | } | ||
| 3137 | } | ||
| 3138 | } else { | ||
| 3139 | root[root.length++] = elements; | ||
| 3140 | } | ||
| 3141 | } | ||
| 3142 | } | ||
| 3143 | } | ||
| 3144 | |||
| 3145 | |||
| 3146 | function jqLiteController(element, name) { | ||
| 3147 | return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); | ||
| 3148 | } | ||
| 3149 | |||
| 3150 | function jqLiteInheritedData(element, name, value) { | ||
| 3151 | // if element is the document object work with the html element instead | ||
| 3152 | // this makes $(document).scope() possible | ||
| 3153 | if (element.nodeType == NODE_TYPE_DOCUMENT) { | ||
| 3154 | element = element.documentElement; | ||
| 3155 | } | ||
| 3156 | var names = isArray(name) ? name : [name]; | ||
| 3157 | |||
| 3158 | while (element) { | ||
| 3159 | for (var i = 0, ii = names.length; i < ii; i++) { | ||
| 3160 | if (isDefined(value = jqLite.data(element, names[i]))) return value; | ||
| 3161 | } | ||
| 3162 | |||
| 3163 | // If dealing with a document fragment node with a host element, and no parent, use the host | ||
| 3164 | // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM | ||
| 3165 | // to lookup parent controllers. | ||
| 3166 | element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); | ||
| 3167 | } | ||
| 3168 | } | ||
| 3169 | |||
| 3170 | function jqLiteEmpty(element) { | ||
| 3171 | jqLiteDealoc(element, true); | ||
| 3172 | while (element.firstChild) { | ||
| 3173 | element.removeChild(element.firstChild); | ||
| 3174 | } | ||
| 3175 | } | ||
| 3176 | |||
| 3177 | function jqLiteRemove(element, keepData) { | ||
| 3178 | if (!keepData) jqLiteDealoc(element); | ||
| 3179 | var parent = element.parentNode; | ||
| 3180 | if (parent) parent.removeChild(element); | ||
| 3181 | } | ||
| 3182 | |||
| 3183 | |||
| 3184 | function jqLiteDocumentLoaded(action, win) { | ||
| 3185 | win = win || window; | ||
| 3186 | if (win.document.readyState === 'complete') { | ||
| 3187 | // Force the action to be run async for consistent behavior | ||
| 3188 | // from the action's point of view | ||
| 3189 | // i.e. it will definitely not be in a $apply | ||
| 3190 | win.setTimeout(action); | ||
| 3191 | } else { | ||
| 3192 | // No need to unbind this handler as load is only ever called once | ||
| 3193 | jqLite(win).on('load', action); | ||
| 3194 | } | ||
| 3195 | } | ||
| 3196 | |||
| 3197 | ////////////////////////////////////////// | ||
| 3198 | // Functions which are declared directly. | ||
| 3199 | ////////////////////////////////////////// | ||
| 3200 | var JQLitePrototype = JQLite.prototype = { | ||
| 3201 | ready: function(fn) { | ||
| 3202 | var fired = false; | ||
| 3203 | |||
| 3204 | function trigger() { | ||
| 3205 | if (fired) return; | ||
| 3206 | fired = true; | ||
| 3207 | fn(); | ||
| 3208 | } | ||
| 3209 | |||
| 3210 | // check if document is already loaded | ||
| 3211 | if (window.document.readyState === 'complete') { | ||
| 3212 | window.setTimeout(trigger); | ||
| 3213 | } else { | ||
| 3214 | this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 | ||
| 3215 | // we can not use jqLite since we are not done loading and jQuery could be loaded later. | ||
| 3216 | // jshint -W064 | ||
| 3217 | JQLite(window).on('load', trigger); // fallback to window.onload for others | ||
| 3218 | // jshint +W064 | ||
| 3219 | } | ||
| 3220 | }, | ||
| 3221 | toString: function() { | ||
| 3222 | var value = []; | ||
| 3223 | forEach(this, function(e) { value.push('' + e);}); | ||
| 3224 | return '[' + value.join(', ') + ']'; | ||
| 3225 | }, | ||
| 3226 | |||
| 3227 | eq: function(index) { | ||
| 3228 | return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); | ||
| 3229 | }, | ||
| 3230 | |||
| 3231 | length: 0, | ||
| 3232 | push: push, | ||
| 3233 | sort: [].sort, | ||
| 3234 | splice: [].splice | ||
| 3235 | }; | ||
| 3236 | |||
| 3237 | ////////////////////////////////////////// | ||
| 3238 | // Functions iterating getter/setters. | ||
| 3239 | // these functions return self on setter and | ||
| 3240 | // value on get. | ||
| 3241 | ////////////////////////////////////////// | ||
| 3242 | var BOOLEAN_ATTR = {}; | ||
| 3243 | forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { | ||
| 3244 | BOOLEAN_ATTR[lowercase(value)] = value; | ||
| 3245 | }); | ||
| 3246 | var BOOLEAN_ELEMENTS = {}; | ||
| 3247 | forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { | ||
| 3248 | BOOLEAN_ELEMENTS[value] = true; | ||
| 3249 | }); | ||
| 3250 | var ALIASED_ATTR = { | ||
| 3251 | 'ngMinlength': 'minlength', | ||
| 3252 | 'ngMaxlength': 'maxlength', | ||
| 3253 | 'ngMin': 'min', | ||
| 3254 | 'ngMax': 'max', | ||
| 3255 | 'ngPattern': 'pattern' | ||
| 3256 | }; | ||
| 3257 | |||
| 3258 | function getBooleanAttrName(element, name) { | ||
| 3259 | // check dom last since we will most likely fail on name | ||
| 3260 | var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; | ||
| 3261 | |||
| 3262 | // booleanAttr is here twice to minimize DOM access | ||
| 3263 | return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; | ||
| 3264 | } | ||
| 3265 | |||
| 3266 | function getAliasedAttrName(name) { | ||
| 3267 | return ALIASED_ATTR[name]; | ||
| 3268 | } | ||
| 3269 | |||
| 3270 | forEach({ | ||
| 3271 | data: jqLiteData, | ||
| 3272 | removeData: jqLiteRemoveData, | ||
| 3273 | hasData: jqLiteHasData, | ||
| 3274 | cleanData: jqLiteCleanData | ||
| 3275 | }, function(fn, name) { | ||
| 3276 | JQLite[name] = fn; | ||
| 3277 | }); | ||
| 3278 | |||
| 3279 | forEach({ | ||
| 3280 | data: jqLiteData, | ||
| 3281 | inheritedData: jqLiteInheritedData, | ||
| 3282 | |||
| 3283 | scope: function(element) { | ||
| 3284 | // Can't use jqLiteData here directly so we stay compatible with jQuery! | ||
| 3285 | return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); | ||
| 3286 | }, | ||
| 3287 | |||
| 3288 | isolateScope: function(element) { | ||
| 3289 | // Can't use jqLiteData here directly so we stay compatible with jQuery! | ||
| 3290 | return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); | ||
| 3291 | }, | ||
| 3292 | |||
| 3293 | controller: jqLiteController, | ||
| 3294 | |||
| 3295 | injector: function(element) { | ||
| 3296 | return jqLiteInheritedData(element, '$injector'); | ||
| 3297 | }, | ||
| 3298 | |||
| 3299 | removeAttr: function(element, name) { | ||
| 3300 | element.removeAttribute(name); | ||
| 3301 | }, | ||
| 3302 | |||
| 3303 | hasClass: jqLiteHasClass, | ||
| 3304 | |||
| 3305 | css: function(element, name, value) { | ||
| 3306 | name = camelCase(name); | ||
| 3307 | |||
| 3308 | if (isDefined(value)) { | ||
| 3309 | element.style[name] = value; | ||
| 3310 | } else { | ||
| 3311 | return element.style[name]; | ||
| 3312 | } | ||
| 3313 | }, | ||
| 3314 | |||
| 3315 | attr: function(element, name, value) { | ||
| 3316 | var nodeType = element.nodeType; | ||
| 3317 | if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) { | ||
| 3318 | return; | ||
| 3319 | } | ||
| 3320 | var lowercasedName = lowercase(name); | ||
| 3321 | if (BOOLEAN_ATTR[lowercasedName]) { | ||
| 3322 | if (isDefined(value)) { | ||
| 3323 | if (!!value) { | ||
| 3324 | element[name] = true; | ||
| 3325 | element.setAttribute(name, lowercasedName); | ||
| 3326 | } else { | ||
| 3327 | element[name] = false; | ||
| 3328 | element.removeAttribute(lowercasedName); | ||
| 3329 | } | ||
| 3330 | } else { | ||
| 3331 | return (element[name] || | ||
| 3332 | (element.attributes.getNamedItem(name) || noop).specified) | ||
| 3333 | ? lowercasedName | ||
| 3334 | : undefined; | ||
| 3335 | } | ||
| 3336 | } else if (isDefined(value)) { | ||
| 3337 | element.setAttribute(name, value); | ||
| 3338 | } else if (element.getAttribute) { | ||
| 3339 | // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code | ||
| 3340 | // some elements (e.g. Document) don't have get attribute, so return undefined | ||
| 3341 | var ret = element.getAttribute(name, 2); | ||
| 3342 | // normalize non-existing attributes to undefined (as jQuery) | ||
| 3343 | return ret === null ? undefined : ret; | ||
| 3344 | } | ||
| 3345 | }, | ||
| 3346 | |||
| 3347 | prop: function(element, name, value) { | ||
| 3348 | if (isDefined(value)) { | ||
| 3349 | element[name] = value; | ||
| 3350 | } else { | ||
| 3351 | return element[name]; | ||
| 3352 | } | ||
| 3353 | }, | ||
| 3354 | |||
| 3355 | text: (function() { | ||
| 3356 | getText.$dv = ''; | ||
| 3357 | return getText; | ||
| 3358 | |||
| 3359 | function getText(element, value) { | ||
| 3360 | if (isUndefined(value)) { | ||
| 3361 | var nodeType = element.nodeType; | ||
| 3362 | return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; | ||
| 3363 | } | ||
| 3364 | element.textContent = value; | ||
| 3365 | } | ||
| 3366 | })(), | ||
| 3367 | |||
| 3368 | val: function(element, value) { | ||
| 3369 | if (isUndefined(value)) { | ||
| 3370 | if (element.multiple && nodeName_(element) === 'select') { | ||
| 3371 | var result = []; | ||
| 3372 | forEach(element.options, function(option) { | ||
| 3373 | if (option.selected) { | ||
| 3374 | result.push(option.value || option.text); | ||
| 3375 | } | ||
| 3376 | }); | ||
| 3377 | return result.length === 0 ? null : result; | ||
| 3378 | } | ||
| 3379 | return element.value; | ||
| 3380 | } | ||
| 3381 | element.value = value; | ||
| 3382 | }, | ||
| 3383 | |||
| 3384 | html: function(element, value) { | ||
| 3385 | if (isUndefined(value)) { | ||
| 3386 | return element.innerHTML; | ||
| 3387 | } | ||
| 3388 | jqLiteDealoc(element, true); | ||
| 3389 | element.innerHTML = value; | ||
| 3390 | }, | ||
| 3391 | |||
| 3392 | empty: jqLiteEmpty | ||
| 3393 | }, function(fn, name) { | ||
| 3394 | /** | ||
| 3395 | * Properties: writes return selection, reads return first value | ||
| 3396 | */ | ||
| 3397 | JQLite.prototype[name] = function(arg1, arg2) { | ||
| 3398 | var i, key; | ||
| 3399 | var nodeCount = this.length; | ||
| 3400 | |||
| 3401 | // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it | ||
| 3402 | // in a way that survives minification. | ||
| 3403 | // jqLiteEmpty takes no arguments but is a setter. | ||
| 3404 | if (fn !== jqLiteEmpty && | ||
| 3405 | (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) { | ||
| 3406 | if (isObject(arg1)) { | ||
| 3407 | |||
| 3408 | // we are a write, but the object properties are the key/values | ||
| 3409 | for (i = 0; i < nodeCount; i++) { | ||
| 3410 | if (fn === jqLiteData) { | ||
| 3411 | // data() takes the whole object in jQuery | ||
| 3412 | fn(this[i], arg1); | ||
| 3413 | } else { | ||
| 3414 | for (key in arg1) { | ||
| 3415 | fn(this[i], key, arg1[key]); | ||
| 3416 | } | ||
| 3417 | } | ||
| 3418 | } | ||
| 3419 | // return self for chaining | ||
| 3420 | return this; | ||
| 3421 | } else { | ||
| 3422 | // we are a read, so read the first child. | ||
| 3423 | // TODO: do we still need this? | ||
| 3424 | var value = fn.$dv; | ||
| 3425 | // Only if we have $dv do we iterate over all, otherwise it is just the first element. | ||
| 3426 | var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount; | ||
| 3427 | for (var j = 0; j < jj; j++) { | ||
| 3428 | var nodeValue = fn(this[j], arg1, arg2); | ||
| 3429 | value = value ? value + nodeValue : nodeValue; | ||
| 3430 | } | ||
| 3431 | return value; | ||
| 3432 | } | ||
| 3433 | } else { | ||
| 3434 | // we are a write, so apply to all children | ||
| 3435 | for (i = 0; i < nodeCount; i++) { | ||
| 3436 | fn(this[i], arg1, arg2); | ||
| 3437 | } | ||
| 3438 | // return self for chaining | ||
| 3439 | return this; | ||
| 3440 | } | ||
| 3441 | }; | ||
| 3442 | }); | ||
| 3443 | |||
| 3444 | function createEventHandler(element, events) { | ||
| 3445 | var eventHandler = function(event, type) { | ||
| 3446 | // jQuery specific api | ||
| 3447 | event.isDefaultPrevented = function() { | ||
| 3448 | return event.defaultPrevented; | ||
| 3449 | }; | ||
| 3450 | |||
| 3451 | var eventFns = events[type || event.type]; | ||
| 3452 | var eventFnsLength = eventFns ? eventFns.length : 0; | ||
| 3453 | |||
| 3454 | if (!eventFnsLength) return; | ||
| 3455 | |||
| 3456 | if (isUndefined(event.immediatePropagationStopped)) { | ||
| 3457 | var originalStopImmediatePropagation = event.stopImmediatePropagation; | ||
| 3458 | event.stopImmediatePropagation = function() { | ||
| 3459 | event.immediatePropagationStopped = true; | ||
| 3460 | |||
| 3461 | if (event.stopPropagation) { | ||
| 3462 | event.stopPropagation(); | ||
| 3463 | } | ||
| 3464 | |||
| 3465 | if (originalStopImmediatePropagation) { | ||
| 3466 | originalStopImmediatePropagation.call(event); | ||
| 3467 | } | ||
| 3468 | }; | ||
| 3469 | } | ||
| 3470 | |||
| 3471 | event.isImmediatePropagationStopped = function() { | ||
| 3472 | return event.immediatePropagationStopped === true; | ||
| 3473 | }; | ||
| 3474 | |||
| 3475 | // Some events have special handlers that wrap the real handler | ||
| 3476 | var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper; | ||
| 3477 | |||
| 3478 | // Copy event handlers in case event handlers array is modified during execution. | ||
| 3479 | if ((eventFnsLength > 1)) { | ||
| 3480 | eventFns = shallowCopy(eventFns); | ||
| 3481 | } | ||
| 3482 | |||
| 3483 | for (var i = 0; i < eventFnsLength; i++) { | ||
| 3484 | if (!event.isImmediatePropagationStopped()) { | ||
| 3485 | handlerWrapper(element, event, eventFns[i]); | ||
| 3486 | } | ||
| 3487 | } | ||
| 3488 | }; | ||
| 3489 | |||
| 3490 | // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all | ||
| 3491 | // events on `element` | ||
| 3492 | eventHandler.elem = element; | ||
| 3493 | return eventHandler; | ||
| 3494 | } | ||
| 3495 | |||
| 3496 | function defaultHandlerWrapper(element, event, handler) { | ||
| 3497 | handler.call(element, event); | ||
| 3498 | } | ||
| 3499 | |||
| 3500 | function specialMouseHandlerWrapper(target, event, handler) { | ||
| 3501 | // Refer to jQuery's implementation of mouseenter & mouseleave | ||
| 3502 | // Read about mouseenter and mouseleave: | ||
| 3503 | // http://www.quirksmode.org/js/events_mouse.html#link8 | ||
| 3504 | var related = event.relatedTarget; | ||
| 3505 | // For mousenter/leave call the handler if related is outside the target. | ||
| 3506 | // NB: No relatedTarget if the mouse left/entered the browser window | ||
| 3507 | if (!related || (related !== target && !jqLiteContains.call(target, related))) { | ||
| 3508 | handler.call(target, event); | ||
| 3509 | } | ||
| 3510 | } | ||
| 3511 | |||
| 3512 | ////////////////////////////////////////// | ||
| 3513 | // Functions iterating traversal. | ||
| 3514 | // These functions chain results into a single | ||
| 3515 | // selector. | ||
| 3516 | ////////////////////////////////////////// | ||
| 3517 | forEach({ | ||
| 3518 | removeData: jqLiteRemoveData, | ||
| 3519 | |||
| 3520 | on: function jqLiteOn(element, type, fn, unsupported) { | ||
| 3521 | if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); | ||
| 3522 | |||
| 3523 | // Do not add event handlers to non-elements because they will not be cleaned up. | ||
| 3524 | if (!jqLiteAcceptsData(element)) { | ||
| 3525 | return; | ||
| 3526 | } | ||
| 3527 | |||
| 3528 | var expandoStore = jqLiteExpandoStore(element, true); | ||
| 3529 | var events = expandoStore.events; | ||
| 3530 | var handle = expandoStore.handle; | ||
| 3531 | |||
| 3532 | if (!handle) { | ||
| 3533 | handle = expandoStore.handle = createEventHandler(element, events); | ||
| 3534 | } | ||
| 3535 | |||
| 3536 | // http://jsperf.com/string-indexof-vs-split | ||
| 3537 | var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; | ||
| 3538 | var i = types.length; | ||
| 3539 | |||
| 3540 | var addHandler = function(type, specialHandlerWrapper, noEventListener) { | ||
| 3541 | var eventFns = events[type]; | ||
| 3542 | |||
| 3543 | if (!eventFns) { | ||
| 3544 | eventFns = events[type] = []; | ||
| 3545 | eventFns.specialHandlerWrapper = specialHandlerWrapper; | ||
| 3546 | if (type !== '$destroy' && !noEventListener) { | ||
| 3547 | addEventListenerFn(element, type, handle); | ||
| 3548 | } | ||
| 3549 | } | ||
| 3550 | |||
| 3551 | eventFns.push(fn); | ||
| 3552 | }; | ||
| 3553 | |||
| 3554 | while (i--) { | ||
| 3555 | type = types[i]; | ||
| 3556 | if (MOUSE_EVENT_MAP[type]) { | ||
| 3557 | addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper); | ||
| 3558 | addHandler(type, undefined, true); | ||
| 3559 | } else { | ||
| 3560 | addHandler(type); | ||
| 3561 | } | ||
| 3562 | } | ||
| 3563 | }, | ||
| 3564 | |||
| 3565 | off: jqLiteOff, | ||
| 3566 | |||
| 3567 | one: function(element, type, fn) { | ||
| 3568 | element = jqLite(element); | ||
| 3569 | |||
| 3570 | //add the listener twice so that when it is called | ||
| 3571 | //you can remove the original function and still be | ||
| 3572 | //able to call element.off(ev, fn) normally | ||
| 3573 | element.on(type, function onFn() { | ||
| 3574 | element.off(type, fn); | ||
| 3575 | element.off(type, onFn); | ||
| 3576 | }); | ||
| 3577 | element.on(type, fn); | ||
| 3578 | }, | ||
| 3579 | |||
| 3580 | replaceWith: function(element, replaceNode) { | ||
| 3581 | var index, parent = element.parentNode; | ||
| 3582 | jqLiteDealoc(element); | ||
| 3583 | forEach(new JQLite(replaceNode), function(node) { | ||
| 3584 | if (index) { | ||
| 3585 | parent.insertBefore(node, index.nextSibling); | ||
| 3586 | } else { | ||
| 3587 | parent.replaceChild(node, element); | ||
| 3588 | } | ||
| 3589 | index = node; | ||
| 3590 | }); | ||
| 3591 | }, | ||
| 3592 | |||
| 3593 | children: function(element) { | ||
| 3594 | var children = []; | ||
| 3595 | forEach(element.childNodes, function(element) { | ||
| 3596 | if (element.nodeType === NODE_TYPE_ELEMENT) { | ||
| 3597 | children.push(element); | ||
| 3598 | } | ||
| 3599 | }); | ||
| 3600 | return children; | ||
| 3601 | }, | ||
| 3602 | |||
| 3603 | contents: function(element) { | ||
| 3604 | return element.contentDocument || element.childNodes || []; | ||
| 3605 | }, | ||
| 3606 | |||
| 3607 | append: function(element, node) { | ||
| 3608 | var nodeType = element.nodeType; | ||
| 3609 | if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; | ||
| 3610 | |||
| 3611 | node = new JQLite(node); | ||
| 3612 | |||
| 3613 | for (var i = 0, ii = node.length; i < ii; i++) { | ||
| 3614 | var child = node[i]; | ||
| 3615 | element.appendChild(child); | ||
| 3616 | } | ||
| 3617 | }, | ||
| 3618 | |||
| 3619 | prepend: function(element, node) { | ||
| 3620 | if (element.nodeType === NODE_TYPE_ELEMENT) { | ||
| 3621 | var index = element.firstChild; | ||
| 3622 | forEach(new JQLite(node), function(child) { | ||
| 3623 | element.insertBefore(child, index); | ||
| 3624 | }); | ||
| 3625 | } | ||
| 3626 | }, | ||
| 3627 | |||
| 3628 | wrap: function(element, wrapNode) { | ||
| 3629 | jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]); | ||
| 3630 | }, | ||
| 3631 | |||
| 3632 | remove: jqLiteRemove, | ||
| 3633 | |||
| 3634 | detach: function(element) { | ||
| 3635 | jqLiteRemove(element, true); | ||
| 3636 | }, | ||
| 3637 | |||
| 3638 | after: function(element, newElement) { | ||
| 3639 | var index = element, parent = element.parentNode; | ||
| 3640 | newElement = new JQLite(newElement); | ||
| 3641 | |||
| 3642 | for (var i = 0, ii = newElement.length; i < ii; i++) { | ||
| 3643 | var node = newElement[i]; | ||
| 3644 | parent.insertBefore(node, index.nextSibling); | ||
| 3645 | index = node; | ||
| 3646 | } | ||
| 3647 | }, | ||
| 3648 | |||
| 3649 | addClass: jqLiteAddClass, | ||
| 3650 | removeClass: jqLiteRemoveClass, | ||
| 3651 | |||
| 3652 | toggleClass: function(element, selector, condition) { | ||
| 3653 | if (selector) { | ||
| 3654 | forEach(selector.split(' '), function(className) { | ||
| 3655 | var classCondition = condition; | ||
| 3656 | if (isUndefined(classCondition)) { | ||
| 3657 | classCondition = !jqLiteHasClass(element, className); | ||
| 3658 | } | ||
| 3659 | (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); | ||
| 3660 | }); | ||
| 3661 | } | ||
| 3662 | }, | ||
| 3663 | |||
| 3664 | parent: function(element) { | ||
| 3665 | var parent = element.parentNode; | ||
| 3666 | return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; | ||
| 3667 | }, | ||
| 3668 | |||
| 3669 | next: function(element) { | ||
| 3670 | return element.nextElementSibling; | ||
| 3671 | }, | ||
| 3672 | |||
| 3673 | find: function(element, selector) { | ||
| 3674 | if (element.getElementsByTagName) { | ||
| 3675 | return element.getElementsByTagName(selector); | ||
| 3676 | } else { | ||
| 3677 | return []; | ||
| 3678 | } | ||
| 3679 | }, | ||
| 3680 | |||
| 3681 | clone: jqLiteClone, | ||
| 3682 | |||
| 3683 | triggerHandler: function(element, event, extraParameters) { | ||
| 3684 | |||
| 3685 | var dummyEvent, eventFnsCopy, handlerArgs; | ||
| 3686 | var eventName = event.type || event; | ||
| 3687 | var expandoStore = jqLiteExpandoStore(element); | ||
| 3688 | var events = expandoStore && expandoStore.events; | ||
| 3689 | var eventFns = events && events[eventName]; | ||
| 3690 | |||
| 3691 | if (eventFns) { | ||
| 3692 | // Create a dummy event to pass to the handlers | ||
| 3693 | dummyEvent = { | ||
| 3694 | preventDefault: function() { this.defaultPrevented = true; }, | ||
| 3695 | isDefaultPrevented: function() { return this.defaultPrevented === true; }, | ||
| 3696 | stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, | ||
| 3697 | isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, | ||
| 3698 | stopPropagation: noop, | ||
| 3699 | type: eventName, | ||
| 3700 | target: element | ||
| 3701 | }; | ||
| 3702 | |||
| 3703 | // If a custom event was provided then extend our dummy event with it | ||
| 3704 | if (event.type) { | ||
| 3705 | dummyEvent = extend(dummyEvent, event); | ||
| 3706 | } | ||
| 3707 | |||
| 3708 | // Copy event handlers in case event handlers array is modified during execution. | ||
| 3709 | eventFnsCopy = shallowCopy(eventFns); | ||
| 3710 | handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; | ||
| 3711 | |||
| 3712 | forEach(eventFnsCopy, function(fn) { | ||
| 3713 | if (!dummyEvent.isImmediatePropagationStopped()) { | ||
| 3714 | fn.apply(element, handlerArgs); | ||
| 3715 | } | ||
| 3716 | }); | ||
| 3717 | } | ||
| 3718 | } | ||
| 3719 | }, function(fn, name) { | ||
| 3720 | /** | ||
| 3721 | * chaining functions | ||
| 3722 | */ | ||
| 3723 | JQLite.prototype[name] = function(arg1, arg2, arg3) { | ||
| 3724 | var value; | ||
| 3725 | |||
| 3726 | for (var i = 0, ii = this.length; i < ii; i++) { | ||
| 3727 | if (isUndefined(value)) { | ||
| 3728 | value = fn(this[i], arg1, arg2, arg3); | ||
| 3729 | if (isDefined(value)) { | ||
| 3730 | // any function which returns a value needs to be wrapped | ||
| 3731 | value = jqLite(value); | ||
| 3732 | } | ||
| 3733 | } else { | ||
| 3734 | jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); | ||
| 3735 | } | ||
| 3736 | } | ||
| 3737 | return isDefined(value) ? value : this; | ||
| 3738 | }; | ||
| 3739 | |||
| 3740 | // bind legacy bind/unbind to on/off | ||
| 3741 | JQLite.prototype.bind = JQLite.prototype.on; | ||
| 3742 | JQLite.prototype.unbind = JQLite.prototype.off; | ||
| 3743 | }); | ||
| 3744 | |||
| 3745 | |||
| 3746 | // Provider for private $$jqLite service | ||
| 3747 | function $$jqLiteProvider() { | ||
| 3748 | this.$get = function $$jqLite() { | ||
| 3749 | return extend(JQLite, { | ||
| 3750 | hasClass: function(node, classes) { | ||
| 3751 | if (node.attr) node = node[0]; | ||
| 3752 | return jqLiteHasClass(node, classes); | ||
| 3753 | }, | ||
| 3754 | addClass: function(node, classes) { | ||
| 3755 | if (node.attr) node = node[0]; | ||
| 3756 | return jqLiteAddClass(node, classes); | ||
| 3757 | }, | ||
| 3758 | removeClass: function(node, classes) { | ||
| 3759 | if (node.attr) node = node[0]; | ||
| 3760 | return jqLiteRemoveClass(node, classes); | ||
| 3761 | } | ||
| 3762 | }); | ||
| 3763 | }; | ||
| 3764 | } | ||
| 3765 | |||
| 3766 | /** | ||
| 3767 | * Computes a hash of an 'obj'. | ||
| 3768 | * Hash of a: | ||
| 3769 | * string is string | ||
| 3770 | * number is number as string | ||
| 3771 | * object is either result of calling $$hashKey function on the object or uniquely generated id, | ||
| 3772 | * that is also assigned to the $$hashKey property of the object. | ||
| 3773 | * | ||
| 3774 | * @param obj | ||
| 3775 | * @returns {string} hash string such that the same input will have the same hash string. | ||
| 3776 | * The resulting string key is in 'type:hashKey' format. | ||
| 3777 | */ | ||
| 3778 | function hashKey(obj, nextUidFn) { | ||
| 3779 | var key = obj && obj.$$hashKey; | ||
| 3780 | |||
| 3781 | if (key) { | ||
| 3782 | if (typeof key === 'function') { | ||
| 3783 | key = obj.$$hashKey(); | ||
| 3784 | } | ||
| 3785 | return key; | ||
| 3786 | } | ||
| 3787 | |||
| 3788 | var objType = typeof obj; | ||
| 3789 | if (objType == 'function' || (objType == 'object' && obj !== null)) { | ||
| 3790 | key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); | ||
| 3791 | } else { | ||
| 3792 | key = objType + ':' + obj; | ||
| 3793 | } | ||
| 3794 | |||
| 3795 | return key; | ||
| 3796 | } | ||
| 3797 | |||
| 3798 | /** | ||
| 3799 | * HashMap which can use objects as keys | ||
| 3800 | */ | ||
| 3801 | function HashMap(array, isolatedUid) { | ||
| 3802 | if (isolatedUid) { | ||
| 3803 | var uid = 0; | ||
| 3804 | this.nextUid = function() { | ||
| 3805 | return ++uid; | ||
| 3806 | }; | ||
| 3807 | } | ||
| 3808 | forEach(array, this.put, this); | ||
| 3809 | } | ||
| 3810 | HashMap.prototype = { | ||
| 3811 | /** | ||
| 3812 | * Store key value pair | ||
| 3813 | * @param key key to store can be any type | ||
| 3814 | * @param value value to store can be any type | ||
| 3815 | */ | ||
| 3816 | put: function(key, value) { | ||
| 3817 | this[hashKey(key, this.nextUid)] = value; | ||
| 3818 | }, | ||
| 3819 | |||
| 3820 | /** | ||
| 3821 | * @param key | ||
| 3822 | * @returns {Object} the value for the key | ||
| 3823 | */ | ||
| 3824 | get: function(key) { | ||
| 3825 | return this[hashKey(key, this.nextUid)]; | ||
| 3826 | }, | ||
| 3827 | |||
| 3828 | /** | ||
| 3829 | * Remove the key/value pair | ||
| 3830 | * @param key | ||
| 3831 | */ | ||
| 3832 | remove: function(key) { | ||
| 3833 | var value = this[key = hashKey(key, this.nextUid)]; | ||
| 3834 | delete this[key]; | ||
| 3835 | return value; | ||
| 3836 | } | ||
| 3837 | }; | ||
| 3838 | |||
| 3839 | var $$HashMapProvider = [function() { | ||
| 3840 | this.$get = [function() { | ||
| 3841 | return HashMap; | ||
| 3842 | }]; | ||
| 3843 | }]; | ||
| 3844 | |||
| 3845 | /** | ||
| 3846 | * @ngdoc function | ||
| 3847 | * @module ng | ||
| 3848 | * @name angular.injector | ||
| 3849 | * @kind function | ||
| 3850 | * | ||
| 3851 | * @description | ||
| 3852 | * Creates an injector object that can be used for retrieving services as well as for | ||
| 3853 | * dependency injection (see {@link guide/di dependency injection}). | ||
| 3854 | * | ||
| 3855 | * @param {Array.<string|Function>} modules A list of module functions or their aliases. See | ||
| 3856 | * {@link angular.module}. The `ng` module must be explicitly added. | ||
| 3857 | * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which | ||
| 3858 | * disallows argument name annotation inference. | ||
| 3859 | * @returns {injector} Injector object. See {@link auto.$injector $injector}. | ||
| 3860 | * | ||
| 3861 | * @example | ||
| 3862 | * Typical usage | ||
| 3863 | * ```js | ||
| 3864 | * // create an injector | ||
| 3865 | * var $injector = angular.injector(['ng']); | ||
| 3866 | * | ||
| 3867 | * // use the injector to kick off your application | ||
| 3868 | * // use the type inference to auto inject arguments, or use implicit injection | ||
| 3869 | * $injector.invoke(function($rootScope, $compile, $document) { | ||
| 3870 | * $compile($document)($rootScope); | ||
| 3871 | * $rootScope.$digest(); | ||
| 3872 | * }); | ||
| 3873 | * ``` | ||
| 3874 | * | ||
| 3875 | * Sometimes you want to get access to the injector of a currently running Angular app | ||
| 3876 | * from outside Angular. Perhaps, you want to inject and compile some markup after the | ||
| 3877 | * application has been bootstrapped. You can do this using the extra `injector()` added | ||
| 3878 | * to JQuery/jqLite elements. See {@link angular.element}. | ||
| 3879 | * | ||
| 3880 | * *This is fairly rare but could be the case if a third party library is injecting the | ||
| 3881 | * markup.* | ||
| 3882 | * | ||
| 3883 | * In the following example a new block of HTML containing a `ng-controller` | ||
| 3884 | * directive is added to the end of the document body by JQuery. We then compile and link | ||
| 3885 | * it into the current AngularJS scope. | ||
| 3886 | * | ||
| 3887 | * ```js | ||
| 3888 | * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); | ||
| 3889 | * $(document.body).append($div); | ||
| 3890 | * | ||
| 3891 | * angular.element(document).injector().invoke(function($compile) { | ||
| 3892 | * var scope = angular.element($div).scope(); | ||
| 3893 | * $compile($div)(scope); | ||
| 3894 | * }); | ||
| 3895 | * ``` | ||
| 3896 | */ | ||
| 3897 | |||
| 3898 | |||
| 3899 | /** | ||
| 3900 | * @ngdoc module | ||
| 3901 | * @name auto | ||
| 3902 | * @installation | ||
| 3903 | * @description | ||
| 3904 | * | ||
| 3905 | * Implicit module which gets automatically added to each {@link auto.$injector $injector}. | ||
| 3906 | */ | ||
| 3907 | |||
| 3908 | var ARROW_ARG = /^([^\(]+?)=>/; | ||
| 3909 | var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; | ||
| 3910 | var FN_ARG_SPLIT = /,/; | ||
| 3911 | var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; | ||
| 3912 | var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; | ||
| 3913 | var $injectorMinErr = minErr('$injector'); | ||
| 3914 | |||
| 3915 | function stringifyFn(fn) { | ||
| 3916 | // Support: Chrome 50-51 only | ||
| 3917 | // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51 | ||
| 3918 | // (See https://github.com/angular/angular.js/issues/14487.) | ||
| 3919 | // TODO (gkalpak): Remove workaround when Chrome v52 is released | ||
| 3920 | return Function.prototype.toString.call(fn) + ' '; | ||
| 3921 | } | ||
| 3922 | |||
| 3923 | function extractArgs(fn) { | ||
| 3924 | var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''), | ||
| 3925 | args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); | ||
| 3926 | return args; | ||
| 3927 | } | ||
| 3928 | |||
| 3929 | function anonFn(fn) { | ||
| 3930 | // For anonymous functions, showing at the very least the function signature can help in | ||
| 3931 | // debugging. | ||
| 3932 | var args = extractArgs(fn); | ||
| 3933 | if (args) { | ||
| 3934 | return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; | ||
| 3935 | } | ||
| 3936 | return 'fn'; | ||
| 3937 | } | ||
| 3938 | |||
| 3939 | function annotate(fn, strictDi, name) { | ||
| 3940 | var $inject, | ||
| 3941 | argDecl, | ||
| 3942 | last; | ||
| 3943 | |||
| 3944 | if (typeof fn === 'function') { | ||
| 3945 | if (!($inject = fn.$inject)) { | ||
| 3946 | $inject = []; | ||
| 3947 | if (fn.length) { | ||
| 3948 | if (strictDi) { | ||
| 3949 | if (!isString(name) || !name) { | ||
| 3950 | name = fn.name || anonFn(fn); | ||
| 3951 | } | ||
| 3952 | throw $injectorMinErr('strictdi', | ||
| 3953 | '{0} is not using explicit annotation and cannot be invoked in strict mode', name); | ||
| 3954 | } | ||
| 3955 | argDecl = extractArgs(fn); | ||
| 3956 | forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { | ||
| 3957 | arg.replace(FN_ARG, function(all, underscore, name) { | ||
| 3958 | $inject.push(name); | ||
| 3959 | }); | ||
| 3960 | }); | ||
| 3961 | } | ||
| 3962 | fn.$inject = $inject; | ||
| 3963 | } | ||
| 3964 | } else if (isArray(fn)) { | ||
| 3965 | last = fn.length - 1; | ||
| 3966 | assertArgFn(fn[last], 'fn'); | ||
| 3967 | $inject = fn.slice(0, last); | ||
| 3968 | } else { | ||
| 3969 | assertArgFn(fn, 'fn', true); | ||
| 3970 | } | ||
| 3971 | return $inject; | ||
| 3972 | } | ||
| 3973 | |||
| 3974 | /////////////////////////////////////// | ||
| 3975 | |||
| 3976 | /** | ||
| 3977 | * @ngdoc service | ||
| 3978 | * @name $injector | ||
| 3979 | * | ||
| 3980 | * @description | ||
| 3981 | * | ||
| 3982 | * `$injector` is used to retrieve object instances as defined by | ||
| 3983 | * {@link auto.$provide provider}, instantiate types, invoke methods, | ||
| 3984 | * and load modules. | ||
| 3985 | * | ||
| 3986 | * The following always holds true: | ||
| 3987 | * | ||
| 3988 | * ```js | ||
| 3989 | * var $injector = angular.injector(); | ||
| 3990 | * expect($injector.get('$injector')).toBe($injector); | ||
| 3991 | * expect($injector.invoke(function($injector) { | ||
| 3992 | * return $injector; | ||
| 3993 | * })).toBe($injector); | ||
| 3994 | * ``` | ||
| 3995 | * | ||
| 3996 | * # Injection Function Annotation | ||
| 3997 | * | ||
| 3998 | * JavaScript does not have annotations, and annotations are needed for dependency injection. The | ||
| 3999 | * following are all valid ways of annotating function with injection arguments and are equivalent. | ||
| 4000 | * | ||
| 4001 | * ```js | ||
| 4002 | * // inferred (only works if code not minified/obfuscated) | ||
| 4003 | * $injector.invoke(function(serviceA){}); | ||
| 4004 | * | ||
| 4005 | * // annotated | ||
| 4006 | * function explicit(serviceA) {}; | ||
| 4007 | * explicit.$inject = ['serviceA']; | ||
| 4008 | * $injector.invoke(explicit); | ||
| 4009 | * | ||
| 4010 | * // inline | ||
| 4011 | * $injector.invoke(['serviceA', function(serviceA){}]); | ||
| 4012 | * ``` | ||
| 4013 | * | ||
| 4014 | * ## Inference | ||
| 4015 | * | ||
| 4016 | * In JavaScript calling `toString()` on a function returns the function definition. The definition | ||
| 4017 | * can then be parsed and the function arguments can be extracted. This method of discovering | ||
| 4018 | * annotations is disallowed when the injector is in strict mode. | ||
| 4019 | * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the | ||
| 4020 | * argument names. | ||
| 4021 | * | ||
| 4022 | * ## `$inject` Annotation | ||
| 4023 | * By adding an `$inject` property onto a function the injection parameters can be specified. | ||
| 4024 | * | ||
| 4025 | * ## Inline | ||
| 4026 | * As an array of injection names, where the last item in the array is the function to call. | ||
| 4027 | */ | ||
| 4028 | |||
| 4029 | /** | ||
| 4030 | * @ngdoc method | ||
| 4031 | * @name $injector#get | ||
| 4032 | * | ||
| 4033 | * @description | ||
| 4034 | * Return an instance of the service. | ||
| 4035 | * | ||
| 4036 | * @param {string} name The name of the instance to retrieve. | ||
| 4037 | * @param {string=} caller An optional string to provide the origin of the function call for error messages. | ||
| 4038 | * @return {*} The instance. | ||
| 4039 | */ | ||
| 4040 | |||
| 4041 | /** | ||
| 4042 | * @ngdoc method | ||
| 4043 | * @name $injector#invoke | ||
| 4044 | * | ||
| 4045 | * @description | ||
| 4046 | * Invoke the method and supply the method arguments from the `$injector`. | ||
| 4047 | * | ||
| 4048 | * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are | ||
| 4049 | * injected according to the {@link guide/di $inject Annotation} rules. | ||
| 4050 | * @param {Object=} self The `this` for the invoked method. | ||
| 4051 | * @param {Object=} locals Optional object. If preset then any argument names are read from this | ||
| 4052 | * object first, before the `$injector` is consulted. | ||
| 4053 | * @returns {*} the value returned by the invoked `fn` function. | ||
| 4054 | */ | ||
| 4055 | |||
| 4056 | /** | ||
| 4057 | * @ngdoc method | ||
| 4058 | * @name $injector#has | ||
| 4059 | * | ||
| 4060 | * @description | ||
| 4061 | * Allows the user to query if the particular service exists. | ||
| 4062 | * | ||
| 4063 | * @param {string} name Name of the service to query. | ||
| 4064 | * @returns {boolean} `true` if injector has given service. | ||
| 4065 | */ | ||
| 4066 | |||
| 4067 | /** | ||
| 4068 | * @ngdoc method | ||
| 4069 | * @name $injector#instantiate | ||
| 4070 | * @description | ||
| 4071 | * Create a new instance of JS type. The method takes a constructor function, invokes the new | ||
| 4072 | * operator, and supplies all of the arguments to the constructor function as specified by the | ||
| 4073 | * constructor annotation. | ||
| 4074 | * | ||
| 4075 | * @param {Function} Type Annotated constructor function. | ||
| 4076 | * @param {Object=} locals Optional object. If preset then any argument names are read from this | ||
| 4077 | * object first, before the `$injector` is consulted. | ||
| 4078 | * @returns {Object} new instance of `Type`. | ||
| 4079 | */ | ||
| 4080 | |||
| 4081 | /** | ||
| 4082 | * @ngdoc method | ||
| 4083 | * @name $injector#annotate | ||
| 4084 | * | ||
| 4085 | * @description | ||
| 4086 | * Returns an array of service names which the function is requesting for injection. This API is | ||
| 4087 | * used by the injector to determine which services need to be injected into the function when the | ||
| 4088 | * function is invoked. There are three ways in which the function can be annotated with the needed | ||
| 4089 | * dependencies. | ||
| 4090 | * | ||
| 4091 | * # Argument names | ||
| 4092 | * | ||
| 4093 | * The simplest form is to extract the dependencies from the arguments of the function. This is done | ||
| 4094 | * by converting the function into a string using `toString()` method and extracting the argument | ||
| 4095 | * names. | ||
| 4096 | * ```js | ||
| 4097 | * // Given | ||
| 4098 | * function MyController($scope, $route) { | ||
| 4099 | * // ... | ||
| 4100 | * } | ||
| 4101 | * | ||
| 4102 | * // Then | ||
| 4103 | * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); | ||
| 4104 | * ``` | ||
| 4105 | * | ||
| 4106 | * You can disallow this method by using strict injection mode. | ||
| 4107 | * | ||
| 4108 | * This method does not work with code minification / obfuscation. For this reason the following | ||
| 4109 | * annotation strategies are supported. | ||
| 4110 | * | ||
| 4111 | * # The `$inject` property | ||
| 4112 | * | ||
| 4113 | * If a function has an `$inject` property and its value is an array of strings, then the strings | ||
| 4114 | * represent names of services to be injected into the function. | ||
| 4115 | * ```js | ||
| 4116 | * // Given | ||
| 4117 | * var MyController = function(obfuscatedScope, obfuscatedRoute) { | ||
| 4118 | * // ... | ||
| 4119 | * } | ||
| 4120 | * // Define function dependencies | ||
| 4121 | * MyController['$inject'] = ['$scope', '$route']; | ||
| 4122 | * | ||
| 4123 | * // Then | ||
| 4124 | * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); | ||
| 4125 | * ``` | ||
| 4126 | * | ||
| 4127 | * # The array notation | ||
| 4128 | * | ||
| 4129 | * It is often desirable to inline Injected functions and that's when setting the `$inject` property | ||
| 4130 | * is very inconvenient. In these situations using the array notation to specify the dependencies in | ||
| 4131 | * a way that survives minification is a better choice: | ||
| 4132 | * | ||
| 4133 | * ```js | ||
| 4134 | * // We wish to write this (not minification / obfuscation safe) | ||
| 4135 | * injector.invoke(function($compile, $rootScope) { | ||
| 4136 | * // ... | ||
| 4137 | * }); | ||
| 4138 | * | ||
| 4139 | * // We are forced to write break inlining | ||
| 4140 | * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { | ||
| 4141 | * // ... | ||
| 4142 | * }; | ||
| 4143 | * tmpFn.$inject = ['$compile', '$rootScope']; | ||
| 4144 | * injector.invoke(tmpFn); | ||
| 4145 | * | ||
| 4146 | * // To better support inline function the inline annotation is supported | ||
| 4147 | * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { | ||
| 4148 | * // ... | ||
| 4149 | * }]); | ||
| 4150 | * | ||
| 4151 | * // Therefore | ||
| 4152 | * expect(injector.annotate( | ||
| 4153 | * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) | ||
| 4154 | * ).toEqual(['$compile', '$rootScope']); | ||
| 4155 | * ``` | ||
| 4156 | * | ||
| 4157 | * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to | ||
| 4158 | * be retrieved as described above. | ||
| 4159 | * | ||
| 4160 | * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. | ||
| 4161 | * | ||
| 4162 | * @returns {Array.<string>} The names of the services which the function requires. | ||
| 4163 | */ | ||
| 4164 | |||
| 4165 | |||
| 4166 | |||
| 4167 | |||
| 4168 | /** | ||
| 4169 | * @ngdoc service | ||
| 4170 | * @name $provide | ||
| 4171 | * | ||
| 4172 | * @description | ||
| 4173 | * | ||
| 4174 | * The {@link auto.$provide $provide} service has a number of methods for registering components | ||
| 4175 | * with the {@link auto.$injector $injector}. Many of these functions are also exposed on | ||
| 4176 | * {@link angular.Module}. | ||
| 4177 | * | ||
| 4178 | * An Angular **service** is a singleton object created by a **service factory**. These **service | ||
| 4179 | * factories** are functions which, in turn, are created by a **service provider**. | ||
| 4180 | * The **service providers** are constructor functions. When instantiated they must contain a | ||
| 4181 | * property called `$get`, which holds the **service factory** function. | ||
| 4182 | * | ||
| 4183 | * When you request a service, the {@link auto.$injector $injector} is responsible for finding the | ||
| 4184 | * correct **service provider**, instantiating it and then calling its `$get` **service factory** | ||
| 4185 | * function to get the instance of the **service**. | ||
| 4186 | * | ||
| 4187 | * Often services have no configuration options and there is no need to add methods to the service | ||
| 4188 | * provider. The provider will be no more than a constructor function with a `$get` property. For | ||
| 4189 | * these cases the {@link auto.$provide $provide} service has additional helper methods to register | ||
| 4190 | * services without specifying a provider. | ||
| 4191 | * | ||
| 4192 | * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the | ||
| 4193 | * {@link auto.$injector $injector} | ||
| 4194 | * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by | ||
| 4195 | * providers and services. | ||
| 4196 | * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by | ||
| 4197 | * services, not providers. | ||
| 4198 | * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function** | ||
| 4199 | * that will be wrapped in a **service provider** object, whose `$get` property will contain the | ||
| 4200 | * given factory function. | ||
| 4201 | * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function** | ||
| 4202 | * that will be wrapped in a **service provider** object, whose `$get` property will instantiate | ||
| 4203 | * a new object using the given constructor function. | ||
| 4204 | * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that | ||
| 4205 | * will be able to modify or replace the implementation of another service. | ||
| 4206 | * | ||
| 4207 | * See the individual methods for more information and examples. | ||
| 4208 | */ | ||
| 4209 | |||
| 4210 | /** | ||
| 4211 | * @ngdoc method | ||
| 4212 | * @name $provide#provider | ||
| 4213 | * @description | ||
| 4214 | * | ||
| 4215 | * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions | ||
| 4216 | * are constructor functions, whose instances are responsible for "providing" a factory for a | ||
| 4217 | * service. | ||
| 4218 | * | ||
| 4219 | * Service provider names start with the name of the service they provide followed by `Provider`. | ||
| 4220 | * For example, the {@link ng.$log $log} service has a provider called | ||
| 4221 | * {@link ng.$logProvider $logProvider}. | ||
| 4222 | * | ||
| 4223 | * Service provider objects can have additional methods which allow configuration of the provider | ||
| 4224 | * and its service. Importantly, you can configure what kind of service is created by the `$get` | ||
| 4225 | * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a | ||
| 4226 | * method {@link ng.$logProvider#debugEnabled debugEnabled} | ||
| 4227 | * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the | ||
| 4228 | * console or not. | ||
| 4229 | * | ||
| 4230 | * @param {string} name The name of the instance. NOTE: the provider will be available under `name + | ||
| 4231 | 'Provider'` key. | ||
| 4232 | * @param {(Object|function())} provider If the provider is: | ||
| 4233 | * | ||
| 4234 | * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using | ||
| 4235 | * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. | ||
| 4236 | * - `Constructor`: a new instance of the provider will be created using | ||
| 4237 | * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. | ||
| 4238 | * | ||
| 4239 | * @returns {Object} registered provider instance | ||
| 4240 | |||
| 4241 | * @example | ||
| 4242 | * | ||
| 4243 | * The following example shows how to create a simple event tracking service and register it using | ||
| 4244 | * {@link auto.$provide#provider $provide.provider()}. | ||
| 4245 | * | ||
| 4246 | * ```js | ||
| 4247 | * // Define the eventTracker provider | ||
| 4248 | * function EventTrackerProvider() { | ||
| 4249 | * var trackingUrl = '/track'; | ||
| 4250 | * | ||
| 4251 | * // A provider method for configuring where the tracked events should been saved | ||
| 4252 | * this.setTrackingUrl = function(url) { | ||
| 4253 | * trackingUrl = url; | ||
| 4254 | * }; | ||
| 4255 | * | ||
| 4256 | * // The service factory function | ||
| 4257 | * this.$get = ['$http', function($http) { | ||
| 4258 | * var trackedEvents = {}; | ||
| 4259 | * return { | ||
| 4260 | * // Call this to track an event | ||
| 4261 | * event: function(event) { | ||
| 4262 | * var count = trackedEvents[event] || 0; | ||
| 4263 | * count += 1; | ||
| 4264 | * trackedEvents[event] = count; | ||
| 4265 | * return count; | ||
| 4266 | * }, | ||
| 4267 | * // Call this to save the tracked events to the trackingUrl | ||
| 4268 | * save: function() { | ||
| 4269 | * $http.post(trackingUrl, trackedEvents); | ||
| 4270 | * } | ||
| 4271 | * }; | ||
| 4272 | * }]; | ||
| 4273 | * } | ||
| 4274 | * | ||
| 4275 | * describe('eventTracker', function() { | ||
| 4276 | * var postSpy; | ||
| 4277 | * | ||
| 4278 | * beforeEach(module(function($provide) { | ||
| 4279 | * // Register the eventTracker provider | ||
| 4280 | * $provide.provider('eventTracker', EventTrackerProvider); | ||
| 4281 | * })); | ||
| 4282 | * | ||
| 4283 | * beforeEach(module(function(eventTrackerProvider) { | ||
| 4284 | * // Configure eventTracker provider | ||
| 4285 | * eventTrackerProvider.setTrackingUrl('/custom-track'); | ||
| 4286 | * })); | ||
| 4287 | * | ||
| 4288 | * it('tracks events', inject(function(eventTracker) { | ||
| 4289 | * expect(eventTracker.event('login')).toEqual(1); | ||
| 4290 | * expect(eventTracker.event('login')).toEqual(2); | ||
| 4291 | * })); | ||
| 4292 | * | ||
| 4293 | * it('saves to the tracking url', inject(function(eventTracker, $http) { | ||
| 4294 | * postSpy = spyOn($http, 'post'); | ||
| 4295 | * eventTracker.event('login'); | ||
| 4296 | * eventTracker.save(); | ||
| 4297 | * expect(postSpy).toHaveBeenCalled(); | ||
| 4298 | * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); | ||
| 4299 | * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); | ||
| 4300 | * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); | ||
| 4301 | * })); | ||
| 4302 | * }); | ||
| 4303 | * ``` | ||
| 4304 | */ | ||
| 4305 | |||
| 4306 | /** | ||
| 4307 | * @ngdoc method | ||
| 4308 | * @name $provide#factory | ||
| 4309 | * @description | ||
| 4310 | * | ||
| 4311 | * Register a **service factory**, which will be called to return the service instance. | ||
| 4312 | * This is short for registering a service where its provider consists of only a `$get` property, | ||
| 4313 | * which is the given service factory function. | ||
| 4314 | * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to | ||
| 4315 | * configure your service in a provider. | ||
| 4316 | * | ||
| 4317 | * @param {string} name The name of the instance. | ||
| 4318 | * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation. | ||
| 4319 | * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. | ||
| 4320 | * @returns {Object} registered provider instance | ||
| 4321 | * | ||
| 4322 | * @example | ||
| 4323 | * Here is an example of registering a service | ||
| 4324 | * ```js | ||
| 4325 | * $provide.factory('ping', ['$http', function($http) { | ||
| 4326 | * return function ping() { | ||
| 4327 | * return $http.send('/ping'); | ||
| 4328 | * }; | ||
| 4329 | * }]); | ||
| 4330 | * ``` | ||
| 4331 | * You would then inject and use this service like this: | ||
| 4332 | * ```js | ||
| 4333 | * someModule.controller('Ctrl', ['ping', function(ping) { | ||
| 4334 | * ping(); | ||
| 4335 | * }]); | ||
| 4336 | * ``` | ||
| 4337 | */ | ||
| 4338 | |||
| 4339 | |||
| 4340 | /** | ||
| 4341 | * @ngdoc method | ||
| 4342 | * @name $provide#service | ||
| 4343 | * @description | ||
| 4344 | * | ||
| 4345 | * Register a **service constructor**, which will be invoked with `new` to create the service | ||
| 4346 | * instance. | ||
| 4347 | * This is short for registering a service where its provider's `$get` property is a factory | ||
| 4348 | * function that returns an instance instantiated by the injector from the service constructor | ||
| 4349 | * function. | ||
| 4350 | * | ||
| 4351 | * Internally it looks a bit like this: | ||
| 4352 | * | ||
| 4353 | * ``` | ||
| 4354 | * { | ||
| 4355 | * $get: function() { | ||
| 4356 | * return $injector.instantiate(constructor); | ||
| 4357 | * } | ||
| 4358 | * } | ||
| 4359 | * ``` | ||
| 4360 | * | ||
| 4361 | * | ||
| 4362 | * You should use {@link auto.$provide#service $provide.service(class)} if you define your service | ||
| 4363 | * as a type/class. | ||
| 4364 | * | ||
| 4365 | * @param {string} name The name of the instance. | ||
| 4366 | * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function) | ||
| 4367 | * that will be instantiated. | ||
| 4368 | * @returns {Object} registered provider instance | ||
| 4369 | * | ||
| 4370 | * @example | ||
| 4371 | * Here is an example of registering a service using | ||
| 4372 | * {@link auto.$provide#service $provide.service(class)}. | ||
| 4373 | * ```js | ||
| 4374 | * var Ping = function($http) { | ||
| 4375 | * this.$http = $http; | ||
| 4376 | * }; | ||
| 4377 | * | ||
| 4378 | * Ping.$inject = ['$http']; | ||
| 4379 | * | ||
| 4380 | * Ping.prototype.send = function() { | ||
| 4381 | * return this.$http.get('/ping'); | ||
| 4382 | * }; | ||
| 4383 | * $provide.service('ping', Ping); | ||
| 4384 | * ``` | ||
| 4385 | * You would then inject and use this service like this: | ||
| 4386 | * ```js | ||
| 4387 | * someModule.controller('Ctrl', ['ping', function(ping) { | ||
| 4388 | * ping.send(); | ||
| 4389 | * }]); | ||
| 4390 | * ``` | ||
| 4391 | */ | ||
| 4392 | |||
| 4393 | |||
| 4394 | /** | ||
| 4395 | * @ngdoc method | ||
| 4396 | * @name $provide#value | ||
| 4397 | * @description | ||
| 4398 | * | ||
| 4399 | * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a | ||
| 4400 | * number, an array, an object or a function. This is short for registering a service where its | ||
| 4401 | * provider's `$get` property is a factory function that takes no arguments and returns the **value | ||
| 4402 | * service**. That also means it is not possible to inject other services into a value service. | ||
| 4403 | * | ||
| 4404 | * Value services are similar to constant services, except that they cannot be injected into a | ||
| 4405 | * module configuration function (see {@link angular.Module#config}) but they can be overridden by | ||
| 4406 | * an Angular {@link auto.$provide#decorator decorator}. | ||
| 4407 | * | ||
| 4408 | * @param {string} name The name of the instance. | ||
| 4409 | * @param {*} value The value. | ||
| 4410 | * @returns {Object} registered provider instance | ||
| 4411 | * | ||
| 4412 | * @example | ||
| 4413 | * Here are some examples of creating value services. | ||
| 4414 | * ```js | ||
| 4415 | * $provide.value('ADMIN_USER', 'admin'); | ||
| 4416 | * | ||
| 4417 | * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); | ||
| 4418 | * | ||
| 4419 | * $provide.value('halfOf', function(value) { | ||
| 4420 | * return value / 2; | ||
| 4421 | * }); | ||
| 4422 | * ``` | ||
| 4423 | */ | ||
| 4424 | |||
| 4425 | |||
| 4426 | /** | ||
| 4427 | * @ngdoc method | ||
| 4428 | * @name $provide#constant | ||
| 4429 | * @description | ||
| 4430 | * | ||
| 4431 | * Register a **constant service** with the {@link auto.$injector $injector}, such as a string, | ||
| 4432 | * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not | ||
| 4433 | * possible to inject other services into a constant. | ||
| 4434 | * | ||
| 4435 | * But unlike {@link auto.$provide#value value}, a constant can be | ||
| 4436 | * injected into a module configuration function (see {@link angular.Module#config}) and it cannot | ||
| 4437 | * be overridden by an Angular {@link auto.$provide#decorator decorator}. | ||
| 4438 | * | ||
| 4439 | * @param {string} name The name of the constant. | ||
| 4440 | * @param {*} value The constant value. | ||
| 4441 | * @returns {Object} registered instance | ||
| 4442 | * | ||
| 4443 | * @example | ||
| 4444 | * Here a some examples of creating constants: | ||
| 4445 | * ```js | ||
| 4446 | * $provide.constant('SHARD_HEIGHT', 306); | ||
| 4447 | * | ||
| 4448 | * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); | ||
| 4449 | * | ||
| 4450 | * $provide.constant('double', function(value) { | ||
| 4451 | * return value * 2; | ||
| 4452 | * }); | ||
| 4453 | * ``` | ||
| 4454 | */ | ||
| 4455 | |||
| 4456 | |||
| 4457 | /** | ||
| 4458 | * @ngdoc method | ||
| 4459 | * @name $provide#decorator | ||
| 4460 | * @description | ||
| 4461 | * | ||
| 4462 | * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function | ||
| 4463 | * intercepts the creation of a service, allowing it to override or modify the behavior of the | ||
| 4464 | * service. The return value of the decorator function may be the original service, or a new service | ||
| 4465 | * that replaces (or wraps and delegates to) the original service. | ||
| 4466 | * | ||
| 4467 | * You can find out more about using decorators in the {@link guide/decorators} guide. | ||
| 4468 | * | ||
| 4469 | * @param {string} name The name of the service to decorate. | ||
| 4470 | * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be | ||
| 4471 | * provided and should return the decorated service instance. The function is called using | ||
| 4472 | * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. | ||
| 4473 | * Local injection arguments: | ||
| 4474 | * | ||
| 4475 | * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured, | ||
| 4476 | * decorated or delegated to. | ||
| 4477 | * | ||
| 4478 | * @example | ||
| 4479 | * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting | ||
| 4480 | * calls to {@link ng.$log#error $log.warn()}. | ||
| 4481 | * ```js | ||
| 4482 | * $provide.decorator('$log', ['$delegate', function($delegate) { | ||
| 4483 | * $delegate.warn = $delegate.error; | ||
| 4484 | * return $delegate; | ||
| 4485 | * }]); | ||
| 4486 | * ``` | ||
| 4487 | */ | ||
| 4488 | |||
| 4489 | |||
| 4490 | function createInjector(modulesToLoad, strictDi) { | ||
| 4491 | strictDi = (strictDi === true); | ||
| 4492 | var INSTANTIATING = {}, | ||
| 4493 | providerSuffix = 'Provider', | ||
| 4494 | path = [], | ||
| 4495 | loadedModules = new HashMap([], true), | ||
| 4496 | providerCache = { | ||
| 4497 | $provide: { | ||
| 4498 | provider: supportObject(provider), | ||
| 4499 | factory: supportObject(factory), | ||
| 4500 | service: supportObject(service), | ||
| 4501 | value: supportObject(value), | ||
| 4502 | constant: supportObject(constant), | ||
| 4503 | decorator: decorator | ||
| 4504 | } | ||
| 4505 | }, | ||
| 4506 | providerInjector = (providerCache.$injector = | ||
| 4507 | createInternalInjector(providerCache, function(serviceName, caller) { | ||
| 4508 | if (angular.isString(caller)) { | ||
| 4509 | path.push(caller); | ||
| 4510 | } | ||
| 4511 | throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); | ||
| 4512 | })), | ||
| 4513 | instanceCache = {}, | ||
| 4514 | protoInstanceInjector = | ||
| 4515 | createInternalInjector(instanceCache, function(serviceName, caller) { | ||
| 4516 | var provider = providerInjector.get(serviceName + providerSuffix, caller); | ||
| 4517 | return instanceInjector.invoke( | ||
| 4518 | provider.$get, provider, undefined, serviceName); | ||
| 4519 | }), | ||
| 4520 | instanceInjector = protoInstanceInjector; | ||
| 4521 | |||
| 4522 | providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) }; | ||
| 4523 | var runBlocks = loadModules(modulesToLoad); | ||
| 4524 | instanceInjector = protoInstanceInjector.get('$injector'); | ||
| 4525 | instanceInjector.strictDi = strictDi; | ||
| 4526 | forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); }); | ||
| 4527 | |||
| 4528 | return instanceInjector; | ||
| 4529 | |||
| 4530 | //////////////////////////////////// | ||
| 4531 | // $provider | ||
| 4532 | //////////////////////////////////// | ||
| 4533 | |||
| 4534 | function supportObject(delegate) { | ||
| 4535 | return function(key, value) { | ||
| 4536 | if (isObject(key)) { | ||
| 4537 | forEach(key, reverseParams(delegate)); | ||
| 4538 | } else { | ||
| 4539 | return delegate(key, value); | ||
| 4540 | } | ||
| 4541 | }; | ||
| 4542 | } | ||
| 4543 | |||
| 4544 | function provider(name, provider_) { | ||
| 4545 | assertNotHasOwnProperty(name, 'service'); | ||
| 4546 | if (isFunction(provider_) || isArray(provider_)) { | ||
| 4547 | provider_ = providerInjector.instantiate(provider_); | ||
| 4548 | } | ||
| 4549 | if (!provider_.$get) { | ||
| 4550 | throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); | ||
| 4551 | } | ||
| 4552 | return providerCache[name + providerSuffix] = provider_; | ||
| 4553 | } | ||
| 4554 | |||
| 4555 | function enforceReturnValue(name, factory) { | ||
| 4556 | return function enforcedReturnValue() { | ||
| 4557 | var result = instanceInjector.invoke(factory, this); | ||
| 4558 | if (isUndefined(result)) { | ||
| 4559 | throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); | ||
| 4560 | } | ||
| 4561 | return result; | ||
| 4562 | }; | ||
| 4563 | } | ||
| 4564 | |||
| 4565 | function factory(name, factoryFn, enforce) { | ||
| 4566 | return provider(name, { | ||
| 4567 | $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn | ||
| 4568 | }); | ||
| 4569 | } | ||
| 4570 | |||
| 4571 | function service(name, constructor) { | ||
| 4572 | return factory(name, ['$injector', function($injector) { | ||
| 4573 | return $injector.instantiate(constructor); | ||
| 4574 | }]); | ||
| 4575 | } | ||
| 4576 | |||
| 4577 | function value(name, val) { return factory(name, valueFn(val), false); } | ||
| 4578 | |||
| 4579 | function constant(name, value) { | ||
| 4580 | assertNotHasOwnProperty(name, 'constant'); | ||
| 4581 | providerCache[name] = value; | ||
| 4582 | instanceCache[name] = value; | ||
| 4583 | } | ||
| 4584 | |||
| 4585 | function decorator(serviceName, decorFn) { | ||
| 4586 | var origProvider = providerInjector.get(serviceName + providerSuffix), | ||
| 4587 | orig$get = origProvider.$get; | ||
| 4588 | |||
| 4589 | origProvider.$get = function() { | ||
| 4590 | var origInstance = instanceInjector.invoke(orig$get, origProvider); | ||
| 4591 | return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); | ||
| 4592 | }; | ||
| 4593 | } | ||
| 4594 | |||
| 4595 | //////////////////////////////////// | ||
| 4596 | // Module Loading | ||
| 4597 | //////////////////////////////////// | ||
| 4598 | function loadModules(modulesToLoad) { | ||
| 4599 | assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); | ||
| 4600 | var runBlocks = [], moduleFn; | ||
| 4601 | forEach(modulesToLoad, function(module) { | ||
| 4602 | if (loadedModules.get(module)) return; | ||
| 4603 | loadedModules.put(module, true); | ||
| 4604 | |||
| 4605 | function runInvokeQueue(queue) { | ||
| 4606 | var i, ii; | ||
| 4607 | for (i = 0, ii = queue.length; i < ii; i++) { | ||
| 4608 | var invokeArgs = queue[i], | ||
| 4609 | provider = providerInjector.get(invokeArgs[0]); | ||
| 4610 | |||
| 4611 | provider[invokeArgs[1]].apply(provider, invokeArgs[2]); | ||
| 4612 | } | ||
| 4613 | } | ||
| 4614 | |||
| 4615 | try { | ||
| 4616 | if (isString(module)) { | ||
| 4617 | moduleFn = angularModule(module); | ||
| 4618 | runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); | ||
| 4619 | runInvokeQueue(moduleFn._invokeQueue); | ||
| 4620 | runInvokeQueue(moduleFn._configBlocks); | ||
| 4621 | } else if (isFunction(module)) { | ||
| 4622 | runBlocks.push(providerInjector.invoke(module)); | ||
| 4623 | } else if (isArray(module)) { | ||
| 4624 | runBlocks.push(providerInjector.invoke(module)); | ||
| 4625 | } else { | ||
| 4626 | assertArgFn(module, 'module'); | ||
| 4627 | } | ||
| 4628 | } catch (e) { | ||
| 4629 | if (isArray(module)) { | ||
| 4630 | module = module[module.length - 1]; | ||
| 4631 | } | ||
| 4632 | if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { | ||
| 4633 | // Safari & FF's stack traces don't contain error.message content | ||
| 4634 | // unlike those of Chrome and IE | ||
| 4635 | // So if stack doesn't contain message, we create a new string that contains both. | ||
| 4636 | // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. | ||
| 4637 | /* jshint -W022 */ | ||
| 4638 | e = e.message + '\n' + e.stack; | ||
| 4639 | } | ||
| 4640 | throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", | ||
| 4641 | module, e.stack || e.message || e); | ||
| 4642 | } | ||
| 4643 | }); | ||
| 4644 | return runBlocks; | ||
| 4645 | } | ||
| 4646 | |||
| 4647 | //////////////////////////////////// | ||
| 4648 | // internal Injector | ||
| 4649 | //////////////////////////////////// | ||
| 4650 | |||
| 4651 | function createInternalInjector(cache, factory) { | ||
| 4652 | |||
| 4653 | function getService(serviceName, caller) { | ||
| 4654 | if (cache.hasOwnProperty(serviceName)) { | ||
| 4655 | if (cache[serviceName] === INSTANTIATING) { | ||
| 4656 | throw $injectorMinErr('cdep', 'Circular dependency found: {0}', | ||
| 4657 | serviceName + ' <- ' + path.join(' <- ')); | ||
| 4658 | } | ||
| 4659 | return cache[serviceName]; | ||
| 4660 | } else { | ||
| 4661 | try { | ||
| 4662 | path.unshift(serviceName); | ||
| 4663 | cache[serviceName] = INSTANTIATING; | ||
| 4664 | return cache[serviceName] = factory(serviceName, caller); | ||
| 4665 | } catch (err) { | ||
| 4666 | if (cache[serviceName] === INSTANTIATING) { | ||
| 4667 | delete cache[serviceName]; | ||
| 4668 | } | ||
| 4669 | throw err; | ||
| 4670 | } finally { | ||
| 4671 | path.shift(); | ||
| 4672 | } | ||
| 4673 | } | ||
| 4674 | } | ||
| 4675 | |||
| 4676 | |||
| 4677 | function injectionArgs(fn, locals, serviceName) { | ||
| 4678 | var args = [], | ||
| 4679 | $inject = createInjector.$$annotate(fn, strictDi, serviceName); | ||
| 4680 | |||
| 4681 | for (var i = 0, length = $inject.length; i < length; i++) { | ||
| 4682 | var key = $inject[i]; | ||
| 4683 | if (typeof key !== 'string') { | ||
| 4684 | throw $injectorMinErr('itkn', | ||
| 4685 | 'Incorrect injection token! Expected service name as string, got {0}', key); | ||
| 4686 | } | ||
| 4687 | args.push(locals && locals.hasOwnProperty(key) ? locals[key] : | ||
| 4688 | getService(key, serviceName)); | ||
| 4689 | } | ||
| 4690 | return args; | ||
| 4691 | } | ||
| 4692 | |||
| 4693 | function isClass(func) { | ||
| 4694 | // IE 9-11 do not support classes and IE9 leaks with the code below. | ||
| 4695 | if (msie <= 11) { | ||
| 4696 | return false; | ||
| 4697 | } | ||
| 4698 | // Support: Edge 12-13 only | ||
| 4699 | // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/ | ||
| 4700 | return typeof func === 'function' | ||
| 4701 | && /^(?:class\b|constructor\()/.test(stringifyFn(func)); | ||
| 4702 | } | ||
| 4703 | |||
| 4704 | function invoke(fn, self, locals, serviceName) { | ||
| 4705 | if (typeof locals === 'string') { | ||
| 4706 | serviceName = locals; | ||
| 4707 | locals = null; | ||
| 4708 | } | ||
| 4709 | |||
| 4710 | var args = injectionArgs(fn, locals, serviceName); | ||
| 4711 | if (isArray(fn)) { | ||
| 4712 | fn = fn[fn.length - 1]; | ||
| 4713 | } | ||
| 4714 | |||
| 4715 | if (!isClass(fn)) { | ||
| 4716 | // http://jsperf.com/angularjs-invoke-apply-vs-switch | ||
| 4717 | // #5388 | ||
| 4718 | return fn.apply(self, args); | ||
| 4719 | } else { | ||
| 4720 | args.unshift(null); | ||
| 4721 | return new (Function.prototype.bind.apply(fn, args))(); | ||
| 4722 | } | ||
| 4723 | } | ||
| 4724 | |||
| 4725 | |||
| 4726 | function instantiate(Type, locals, serviceName) { | ||
| 4727 | // Check if Type is annotated and use just the given function at n-1 as parameter | ||
| 4728 | // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); | ||
| 4729 | var ctor = (isArray(Type) ? Type[Type.length - 1] : Type); | ||
| 4730 | var args = injectionArgs(Type, locals, serviceName); | ||
| 4731 | // Empty object at position 0 is ignored for invocation with `new`, but required. | ||
| 4732 | args.unshift(null); | ||
| 4733 | return new (Function.prototype.bind.apply(ctor, args))(); | ||
| 4734 | } | ||
| 4735 | |||
| 4736 | |||
| 4737 | return { | ||
| 4738 | invoke: invoke, | ||
| 4739 | instantiate: instantiate, | ||
| 4740 | get: getService, | ||
| 4741 | annotate: createInjector.$$annotate, | ||
| 4742 | has: function(name) { | ||
| 4743 | return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); | ||
| 4744 | } | ||
| 4745 | }; | ||
| 4746 | } | ||
| 4747 | } | ||
| 4748 | |||
| 4749 | createInjector.$$annotate = annotate; | ||
| 4750 | |||
| 4751 | /** | ||
| 4752 | * @ngdoc provider | ||
| 4753 | * @name $anchorScrollProvider | ||
| 4754 | * | ||
| 4755 | * @description | ||
| 4756 | * Use `$anchorScrollProvider` to disable automatic scrolling whenever | ||
| 4757 | * {@link ng.$location#hash $location.hash()} changes. | ||
| 4758 | */ | ||
| 4759 | function $AnchorScrollProvider() { | ||
| 4760 | |||
| 4761 | var autoScrollingEnabled = true; | ||
| 4762 | |||
| 4763 | /** | ||
| 4764 | * @ngdoc method | ||
| 4765 | * @name $anchorScrollProvider#disableAutoScrolling | ||
| 4766 | * | ||
| 4767 | * @description | ||
| 4768 | * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to | ||
| 4769 | * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> | ||
| 4770 | * Use this method to disable automatic scrolling. | ||
| 4771 | * | ||
| 4772 | * If automatic scrolling is disabled, one must explicitly call | ||
| 4773 | * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the | ||
| 4774 | * current hash. | ||
| 4775 | */ | ||
| 4776 | this.disableAutoScrolling = function() { | ||
| 4777 | autoScrollingEnabled = false; | ||
| 4778 | }; | ||
| 4779 | |||
| 4780 | /** | ||
| 4781 | * @ngdoc service | ||
| 4782 | * @name $anchorScroll | ||
| 4783 | * @kind function | ||
| 4784 | * @requires $window | ||
| 4785 | * @requires $location | ||
| 4786 | * @requires $rootScope | ||
| 4787 | * | ||
| 4788 | * @description | ||
| 4789 | * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the | ||
| 4790 | * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified | ||
| 4791 | * in the | ||
| 4792 | * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document). | ||
| 4793 | * | ||
| 4794 | * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to | ||
| 4795 | * match any anchor whenever it changes. This can be disabled by calling | ||
| 4796 | * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. | ||
| 4797 | * | ||
| 4798 | * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a | ||
| 4799 | * vertical scroll-offset (either fixed or dynamic). | ||
| 4800 | * | ||
| 4801 | * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of | ||
| 4802 | * {@link ng.$location#hash $location.hash()} will be used. | ||
| 4803 | * | ||
| 4804 | * @property {(number|function|jqLite)} yOffset | ||
| 4805 | * If set, specifies a vertical scroll-offset. This is often useful when there are fixed | ||
| 4806 | * positioned elements at the top of the page, such as navbars, headers etc. | ||
| 4807 | * | ||
| 4808 | * `yOffset` can be specified in various ways: | ||
| 4809 | * - **number**: A fixed number of pixels to be used as offset.<br /><br /> | ||
| 4810 | * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return | ||
| 4811 | * a number representing the offset (in pixels).<br /><br /> | ||
| 4812 | * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from | ||
| 4813 | * the top of the page to the element's bottom will be used as offset.<br /> | ||
| 4814 | * **Note**: The element will be taken into account only as long as its `position` is set to | ||
| 4815 | * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust | ||
| 4816 | * their height and/or positioning according to the viewport's size. | ||
| 4817 | * | ||
| 4818 | * <br /> | ||
| 4819 | * <div class="alert alert-warning"> | ||
| 4820 | * In order for `yOffset` to work properly, scrolling should take place on the document's root and | ||
| 4821 | * not some child element. | ||
| 4822 | * </div> | ||
| 4823 | * | ||
| 4824 | * @example | ||
| 4825 | <example module="anchorScrollExample"> | ||
| 4826 | <file name="index.html"> | ||
| 4827 | <div id="scrollArea" ng-controller="ScrollController"> | ||
| 4828 | <a ng-click="gotoBottom()">Go to bottom</a> | ||
| 4829 | <a id="bottom"></a> You're at the bottom! | ||
| 4830 | </div> | ||
| 4831 | </file> | ||
| 4832 | <file name="script.js"> | ||
| 4833 | angular.module('anchorScrollExample', []) | ||
| 4834 | .controller('ScrollController', ['$scope', '$location', '$anchorScroll', | ||
| 4835 | function ($scope, $location, $anchorScroll) { | ||
| 4836 | $scope.gotoBottom = function() { | ||
| 4837 | // set the location.hash to the id of | ||
| 4838 | // the element you wish to scroll to. | ||
| 4839 | $location.hash('bottom'); | ||
| 4840 | |||
| 4841 | // call $anchorScroll() | ||
| 4842 | $anchorScroll(); | ||
| 4843 | }; | ||
| 4844 | }]); | ||
| 4845 | </file> | ||
| 4846 | <file name="style.css"> | ||
| 4847 | #scrollArea { | ||
| 4848 | height: 280px; | ||
| 4849 | overflow: auto; | ||
| 4850 | } | ||
| 4851 | |||
| 4852 | #bottom { | ||
| 4853 | display: block; | ||
| 4854 | margin-top: 2000px; | ||
| 4855 | } | ||
| 4856 | </file> | ||
| 4857 | </example> | ||
| 4858 | * | ||
| 4859 | * <hr /> | ||
| 4860 | * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). | ||
| 4861 | * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. | ||
| 4862 | * | ||
| 4863 | * @example | ||
| 4864 | <example module="anchorScrollOffsetExample"> | ||
| 4865 | <file name="index.html"> | ||
| 4866 | <div class="fixed-header" ng-controller="headerCtrl"> | ||
| 4867 | <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]"> | ||
| 4868 | Go to anchor {{x}} | ||
| 4869 | </a> | ||
| 4870 | </div> | ||
| 4871 | <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]"> | ||
| 4872 | Anchor {{x}} of 5 | ||
| 4873 | </div> | ||
| 4874 | </file> | ||
| 4875 | <file name="script.js"> | ||
| 4876 | angular.module('anchorScrollOffsetExample', []) | ||
| 4877 | .run(['$anchorScroll', function($anchorScroll) { | ||
| 4878 | $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels | ||
| 4879 | }]) | ||
| 4880 | .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', | ||
| 4881 | function ($anchorScroll, $location, $scope) { | ||
| 4882 | $scope.gotoAnchor = function(x) { | ||
| 4883 | var newHash = 'anchor' + x; | ||
| 4884 | if ($location.hash() !== newHash) { | ||
| 4885 | // set the $location.hash to `newHash` and | ||
| 4886 | // $anchorScroll will automatically scroll to it | ||
| 4887 | $location.hash('anchor' + x); | ||
| 4888 | } else { | ||
| 4889 | // call $anchorScroll() explicitly, | ||
| 4890 | // since $location.hash hasn't changed | ||
| 4891 | $anchorScroll(); | ||
| 4892 | } | ||
| 4893 | }; | ||
| 4894 | } | ||
| 4895 | ]); | ||
| 4896 | </file> | ||
| 4897 | <file name="style.css"> | ||
| 4898 | body { | ||
| 4899 | padding-top: 50px; | ||
| 4900 | } | ||
| 4901 | |||
| 4902 | .anchor { | ||
| 4903 | border: 2px dashed DarkOrchid; | ||
| 4904 | padding: 10px 10px 200px 10px; | ||
| 4905 | } | ||
| 4906 | |||
| 4907 | .fixed-header { | ||
| 4908 | background-color: rgba(0, 0, 0, 0.2); | ||
| 4909 | height: 50px; | ||
| 4910 | position: fixed; | ||
| 4911 | top: 0; left: 0; right: 0; | ||
| 4912 | } | ||
| 4913 | |||
| 4914 | .fixed-header > a { | ||
| 4915 | display: inline-block; | ||
| 4916 | margin: 5px 15px; | ||
| 4917 | } | ||
| 4918 | </file> | ||
| 4919 | </example> | ||
| 4920 | */ | ||
| 4921 | this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { | ||
| 4922 | var document = $window.document; | ||
| 4923 | |||
| 4924 | // Helper function to get first anchor from a NodeList | ||
| 4925 | // (using `Array#some()` instead of `angular#forEach()` since it's more performant | ||
| 4926 | // and working in all supported browsers.) | ||
| 4927 | function getFirstAnchor(list) { | ||
| 4928 | var result = null; | ||
| 4929 | Array.prototype.some.call(list, function(element) { | ||
| 4930 | if (nodeName_(element) === 'a') { | ||
| 4931 | result = element; | ||
| 4932 | return true; | ||
| 4933 | } | ||
| 4934 | }); | ||
| 4935 | return result; | ||
| 4936 | } | ||
| 4937 | |||
| 4938 | function getYOffset() { | ||
| 4939 | |||
| 4940 | var offset = scroll.yOffset; | ||
| 4941 | |||
| 4942 | if (isFunction(offset)) { | ||
| 4943 | offset = offset(); | ||
| 4944 | } else if (isElement(offset)) { | ||
| 4945 | var elem = offset[0]; | ||
| 4946 | var style = $window.getComputedStyle(elem); | ||
| 4947 | if (style.position !== 'fixed') { | ||
| 4948 | offset = 0; | ||
| 4949 | } else { | ||
| 4950 | offset = elem.getBoundingClientRect().bottom; | ||
| 4951 | } | ||
| 4952 | } else if (!isNumber(offset)) { | ||
| 4953 | offset = 0; | ||
| 4954 | } | ||
| 4955 | |||
| 4956 | return offset; | ||
| 4957 | } | ||
| 4958 | |||
| 4959 | function scrollTo(elem) { | ||
| 4960 | if (elem) { | ||
| 4961 | elem.scrollIntoView(); | ||
| 4962 | |||
| 4963 | var offset = getYOffset(); | ||
| 4964 | |||
| 4965 | if (offset) { | ||
| 4966 | // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. | ||
| 4967 | // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the | ||
| 4968 | // top of the viewport. | ||
| 4969 | // | ||
| 4970 | // IF the number of pixels from the top of `elem` to the end of the page's content is less | ||
| 4971 | // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some | ||
| 4972 | // way down the page. | ||
| 4973 | // | ||
| 4974 | // This is often the case for elements near the bottom of the page. | ||
| 4975 | // | ||
| 4976 | // In such cases we do not need to scroll the whole `offset` up, just the difference between | ||
| 4977 | // the top of the element and the offset, which is enough to align the top of `elem` at the | ||
| 4978 | // desired position. | ||
| 4979 | var elemTop = elem.getBoundingClientRect().top; | ||
| 4980 | $window.scrollBy(0, elemTop - offset); | ||
| 4981 | } | ||
| 4982 | } else { | ||
| 4983 | $window.scrollTo(0, 0); | ||
| 4984 | } | ||
| 4985 | } | ||
| 4986 | |||
| 4987 | function scroll(hash) { | ||
| 4988 | hash = isString(hash) ? hash : $location.hash(); | ||
| 4989 | var elm; | ||
| 4990 | |||
| 4991 | // empty hash, scroll to the top of the page | ||
| 4992 | if (!hash) scrollTo(null); | ||
| 4993 | |||
| 4994 | // element with given id | ||
| 4995 | else if ((elm = document.getElementById(hash))) scrollTo(elm); | ||
| 4996 | |||
| 4997 | // first anchor with given name :-D | ||
| 4998 | else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); | ||
| 4999 | |||
26769 more lines aren’t shown here.