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.