時間:2014年12月15日 14:15:10javascript
1 /** 2 * @license AngularJS v1.3.0-beta.15 3 * (c) 2010-2014 Google, Inc. http://angularjs.org 4 * License: MIT 5 */ 6 (function(window, document, undefined) {'use strict'; 7 8 /** 9 * @description 10 * 11 * This object provides a utility for producing rich Error messages within 12 * Angular. It can be called as follows: 13 *當Angular在發生錯誤的時候,這個對象提供一個處理錯誤的工具類,它的調用方式以下: 14 * 例如: 15 * var exampleMinErr = minErr('example'); 16 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); 17 * 18 * The above creates an instance of minErr in the example namespace. The 19 * resulting error will have a namespaced error code of example.one. The 20 * resulting error will replace {0} with the value of foo, and {1} with the 21 * value of bar. The object is not restricted in the number of arguments it can 22 * take. 23 *在上面的代碼中,在example命名空間中建立了一個minErr的實例。錯誤的結果會在example, 24 *裏面生成一個錯誤碼的example.one命名空間。這個錯誤的結果會用foo的值和bar的值, 25 *來進行替換前面的點位符{0},{1}.這個對象不嚴格必須給定錯誤參數,所以錯誤參數無關緊要。 26 * 27 * If fewer arguments are specified than necessary for interpolation, the extra 28 * interpolation markers will be preserved in the final string. 29 *若是指定的錯誤對象中的錯誤參數,則它會保留在最終生成的字符串中。 30 * 31 * Since data will be parsed statically during a build step, some restrictions 32 * are applied with respect to how minErr instances are created and called. 33 * Instances should have names of the form namespaceMinErr for a minErr created 34 * using minErr('namespace') . Error codes, namespaces and template strings 35 * should all be static strings, not variables or general expressions. 36 *在編譯階段,錯誤參數中的數據會本身生成與解析,錯誤對象中的一些限制將會監視錯誤對象的 37 實例該如何被建立與如何被調用。 38 * 39 * @param {string} module The namespace to use for the new minErr instance. 40 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance 41 */ 42 43 //module:模板名稱,將會用於新的錯誤對象的實例中 44 function minErr(module) { 45 return function () {//直接進行返回操做 46 //獲取錯誤參數中的錯誤標識符,如,上面示例中的one 47 var code = arguments[0], 48 //獲取錯誤對象模板的前綴 49 prefix = '[' + (module ? module + ':' : '') + code + '] ', 50 template = arguments[1], 51 templateArgs = arguments, 52 //這個屬於偏置函數 53 stringify = function (obj) { 54 if (typeof obj === 'function') {//若是參數是一個函數 55 //返回的結果,如:{'item in items '}-->'' 56 return obj.toString().replace(/ \{[\s\S]*$/, ''); 57 } else if (typeof obj === 'undefined') { 58 return 'undefined'; 59 } else if (typeof obj !== 'string') { 60 return JSON.stringify(obj);//強制參數轉換爲Json的字符串形式 61 } 62 return obj; 63 }, 64 message, i; 65 66 67 message = prefix + template.replace(/\{\d+\}/g, function (match) { 68 var index = +match.slice(1, -1), arg; 69 70 if (index + 2 < templateArgs.length) { 71 arg = templateArgs[index + 2]; 72 if (typeof arg === 'function') { 73 return arg.toString().replace(/ ?\{[\s\S]*$/, ''); 74 } else if (typeof arg === 'undefined') { 75 return 'undefined'; 76 } else if (typeof arg !== 'string') { 77 return toJson(arg); 78 } 79 return arg; 80 } 81 return match; 82 }); 83 84 message = message + '\nhttp://errors.angularjs.org/1.3.0-beta.15/' + 85 (module ? module + '/' : '') + code; 86 for (i = 2; i < arguments.length; i++) { 87 message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + 88 encodeURIComponent(stringify(arguments[i])); 89 } 90 91 return new Error(message); 92 }; 93 } 94 95 /* We need to tell jshint what variables are being exported */ 96 /* global angular: true, 97 msie: true, 98 jqLite: true, 99 jQuery: true, 100 slice: true, 101 push: true, 102 toString: true, 103 ngMinErr: true, 104 angularModule: true, 105 nodeName_: true, 106 uid: true, 107 REGEX_STRING_REGEXP: true, 108 VALIDITY_STATE_PROPERTY: true, 109 110 lowercase: true, 111 uppercase: true, 112 manualLowercase: true, 113 manualUppercase: true, 114 nodeName_: true, 115 isArrayLike: true, 116 forEach: true, 117 sortedKeys: true, 118 forEachSorted: true, 119 reverseParams: true, 120 nextUid: true, 121 setHashKey: true, 122 extend: true, 123 int: true, 124 inherit: true, 125 noop: true, 126 identity: true, 127 valueFn: true, 128 isUndefined: true, 129 isDefined: true, 130 isObject: true, 131 isString: true, 132 isNumber: true, 133 isDate: true, 134 isArray: true, 135 isFunction: true, 136 isRegExp: true, 137 isWindow: true, 138 isScope: true, 139 isFile: true, 140 isBlob: true, 141 isBoolean: true, 142 trim: true, 143 isElement: true, 144 makeMap: true, 145 map: true, 146 size: true, 147 includes: true, 148 indexOf: true, 149 arrayRemove: true, 150 isLeafNode: true, 151 copy: true, 152 shallowCopy: true, 153 equals: true, 154 csp: true, 155 concat: true, 156 sliceArgs: true, 157 bind: true, 158 toJsonReplacer: true, 159 toJson: true, 160 fromJson: true, 161 startingTag: true, 162 tryDecodeURIComponent: true, 163 parseKeyValue: true, 164 toKeyValue: true, 165 encodeUriSegment: true, 166 encodeUriQuery: true, 167 angularInit: true, 168 bootstrap: true, 169 snake_case: true, 170 bindJQuery: true, 171 assertArg: true, 172 assertArgFn: true, 173 assertNotHasOwnProperty: true, 174 getter: true, 175 getBlockElements: true, 176 hasOwnProperty: true, 177 */ 178 179 //////////////////////////////////// 180 181 /** 182 * @ngdoc module 183 * @name ng 184 * @module ng 185 * @description 186 * 187 * # ng (core module) 188 * The ng module is loaded by default when an AngularJS application is started. The module itself 189 * contains the essential components for an AngularJS application to function. The table below 190 * lists a high level breakdown of each of the services/factories, filters, directives and testing 191 * components available within this core module. 192 * 193 * <div doc-module-components="ng"></div> 194 * 195 * ng模板默認在Angular應用程序啓動的時候會被自動加載。這個模板包含了Angular應用程序運行所必須的組件。 196 * 下表列出了在這個核心的模塊中所包含的服務/工廠,過濾器,指令和一些測試的組件 197 */ 198 199 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; 200 201 // The name of a form control's ValidityState property. 202 // This is used so that it's possible for internal tests to create mock ValidityStates. 203 var VALIDITY_STATE_PROPERTY = 'validity'; 204 205 /** 206 * @ngdoc function 207 * @name angular.lowercase 208 * @module ng 209 * @kind function 210 * 211 * @description Converts the specified string to lowercase. 212 * @param {string} string String to be converted to lowercase. 213 * @returns {string} Lowercased string. 214 */ 215 //轉換指定的字符串爲小寫 216 var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; 217 var hasOwnProperty = Object.prototype.hasOwnProperty; 218 219 /** 220 * @ngdoc function 221 * @name angular.uppercase 222 * @module ng 223 * @kind function 224 * 225 * @description Converts the specified string to uppercase. 226 * @param {string} string String to be converted to uppercase. 227 * @returns {string} Uppercased string. 228 */ 229 //轉換指定的字符串爲大寫 230 var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; 231 232 233 var manualLowercase = function(s) { 234 /* jshint bitwise: false */ 235 return isString(s) 236 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) 237 : s; 238 }; 239 var manualUppercase = function(s) { 240 /* jshint bitwise: false */ 241 return isString(s) 242 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) 243 : s; 244 }; 245 246 247 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish 248 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods 249 // with correct but slower alternatives. 250 if ('i' !== 'I'.toLowerCase()) { 251 lowercase = manualLowercase; 252 uppercase = manualUppercase; 253 } 254 255 256 var /** holds major version number for IE or NaN for real browsers */ 257 msie, 258 jqLite, // delay binding since jQuery could be loaded after us. 259 jQuery, // delay binding 260 slice = [].slice,//取出指定位置的數組數據 261 push = [].push,//把指定的數據放到數據裏面 262 toString = Object.prototype.toString,//重寫Object原型中的toString方法 263 ngMinErr = minErr('ng'),//定義一個全局的錯誤對象,對應的錯誤參數爲,ng 264 265 /** @name angular */ 266 //判斷系統是否註冊了angular應用程序,若是沒有註冊則註冊angular應用程序的爲空對象 267 angular = window.angular || (window.angular = {}), 268 angularModule, 269 nodeName_, 270 uid = 0; 271 272 /** 273 * IE 11 changed the format of the UserAgent string. 274 * See http://msdn.microsoft.com/en-us/library/ms537503.aspx 275 */ 276 //判斷瀏覽器的類型 277 msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); 278 if (isNaN(msie)) { 279 msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); 280 } 281 282 283 /** 284 * @private 285 * @param {*} obj 286 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, 287 * String ...) 288 */ 289 //判斷傳入的對象obj是不是數組或有相似數組的行爲,它能夠是json字符串或數組 290 function isArrayLike(obj) { 291 if (obj == null || isWindow(obj)) {//爲空或window對象,則返回false 292 return false; 293 } 294 295 var length = obj.length;//獲取數組的長度 296 297 if (obj.nodeType === 1 && length) {//判斷obj的節點類型,若是爲元素,則返回true 298 return true; 299 } 300 301 return isString(obj) || isArray(obj) || length === 0 || 302 typeof length === 'number' && length > 0 && (length - 1) in obj; 303 } 304 305 /** 306 * @ngdoc function 307 * @name angular.forEach 308 * @module ng 309 * @kind function 310 * 311 * @description 312 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an 313 * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` 314 * is the value of an object property or an array element and `key` is the object property key or 315 * array element index. Specifying a `context` for the function is optional. 316 * 317 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters 318 * using the `hasOwnProperty` method. 319 * 320 ```js 321 var values = {name: 'misko', gender: 'male'}; 322 var log = []; 323 angular.forEach(values, function(value, key) { 324 this.push(key + ': ' + value); 325 }, log); 326 expect(log).toEqual(['name: misko', 'gender: male']); 327 ``` 328 * 329 * @param {Object|Array} obj Object to iterate over.//能夠是一個對象或一個數組 330 * @param {Function} iterator Iterator function.//迭代器是一個函數 331 * @param {Object=} context Object to become context (`this`) for the iterator function.//這個對象是一個上下文對象充當this的角色 332 * @returns {Object|Array} Reference to `obj`. 333 */ 334 //其中context可選參數,iterator是一個迭代器,它是一個函數 335 //它不能獲取的繼承的屬性,由於程序中用hasOwnProperty來過濾了 336 function forEach(obj, iterator, context) { 337 var key, length; 338 if (obj) { 339 if (isFunction(obj)) { 340 for (key in obj) { 341 // Need to check if hasOwnProperty exists, 342 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function 343 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { 344 iterator.call(context, obj[key], key); 345 } 346 } 347 } else if (isArray(obj) || isArrayLike(obj)) { 348 for (key = 0, length = obj.length; key < length; key++) { 349 iterator.call(context, obj[key], key); 350 } 351 } else if (obj.forEach && obj.forEach !== forEach) { 352 obj.forEach(iterator, context); 353 } else { 354 for (key in obj) { 355 if (obj.hasOwnProperty(key)) { 356 iterator.call(context, obj[key], key); 357 } 358 } 359 } 360 } 361 return obj; 362 } 363 364 function sortedKeys(obj) {//按照鍵來排序 365 var keys = []; 366 for (var key in obj) { 367 if (obj.hasOwnProperty(key)) { 368 keys.push(key); 369 } 370 } 371 return keys.sort();//採用數組默認的排序方式 372 } 373 374 function forEachSorted(obj, iterator, context) {//先按照鍵排序,而後再遍歷|迭代 375 var keys = sortedKeys(obj); 376 for ( var i = 0; i < keys.length; i++) { 377 iterator.call(context, obj[keys[i]], keys[i]); 378 } 379 return keys; 380 } 381 382 383 /** 384 * when using forEach the params are value, key, but it is often useful to have key, value. 385 * @param {function(string, *)} iteratorFn 386 * @returns {function(*, string)} 387 */ 388 function reverseParams(iteratorFn) { 389 return function(value, key) { iteratorFn(key, value); }; 390 } 391 392 /** 393 * A consistent way of creating unique IDs in angular. 394 * 395 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before 396 * we hit number precision issues in JavaScript. 397 * 398 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M 399 * 400 * @returns {number} an unique alpha-numeric string 401 */ 402 function nextUid() {//生成一個常量,它在andular裏面是惟一的 403 return ++uid; 404 } 405 406 407 /** 408 * Set or clear the hashkey for an object. 409 * @param obj object//參數是一個對象 410 * @param h the hashkey (!truthy to delete the hashkey) 411 */ 412 function setHashKey(obj, h) { 413 if (h) {//若是存在就設置 414 obj.$$hashKey = h; 415 } 416 else {//若是不存在則刪除 417 delete obj.$$hashKey; 418 } 419 } 420 421 /** 422 * @ngdoc function 423 * @name angular.extend 424 * @module ng 425 * @kind function 426 * 427 * @description 428 * Extends the destination object `dst` by copying all of the properties from the `src` object(s) 429 * to `dst`. You can specify multiple `src` objects. 430 * 431 * @param {Object} dst Destination object.//最終的對象 432 * @param {...Object} src Source object(s).//源對象 433 * @returns {Object} Reference to `dst`.//返回最終的對象 434 */ 435 function extend(dst) {//在javascript中實現繼承 436 var h = dst.$$hashKey; 437 forEach(arguments, function(obj) {//這裏採用拷貝屬性的方式實現繼承 438 if (obj !== dst) { 439 forEach(obj, function(value, key) { 440 dst[key] = value; 441 }); 442 } 443 }); 444 445 setHashKey(dst,h); 446 return dst; 447 } 448 449 function int(str) {//轉換爲整形 450 return parseInt(str, 10); 451 } 452 453 454 function inherit(parent, extra) {//繼承 455 return extend(new (extend(function() {}, {prototype:parent}))(), extra); 456 } 457 458 /** 459 * @ngdoc function 460 * @name angular.noop 461 * @module ng 462 * @kind function 463 * 464 * @description 465 * A function that performs no operations. This function can be useful when writing code in the 466 * functional style. 467 ```js 468 function foo(callback) { 469 var result = calculateResult(); 470 (callback || angular.noop)(result); 471 } 472 ``` 473 */ 474 function noop() {}//在寫樣式代碼的時候會被用到 475 noop.$inject = []; 476 477 478 /** 479 * @ngdoc function 480 * @name angular.identity 481 * @module ng 482 * @kind function 483 * 484 * @description 485 * A function that returns its first argument. This function is useful when writing code in the 486 * functional style. 487 * 488 ```js 489 function transformer(transformationFn, value) { 490 return (transformationFn || angular.identity)(value); 491 }; 492 ``` 493 */ 494 function identity($) {return $;}//在寫樣式代碼的時候會被用到//定義angular的標識 495 identity.$inject = []; 496 497 498 function valueFn(value) {return function() {return value;};} 499 500 /** 501 * @ngdoc function 502 * @name angular.isUndefined 503 * @module ng 504 * @kind function 505 * 506 * @description 507 * Determines if a reference is undefined. 508 * 509 * @param {*} value Reference to check. 510 * @returns {boolean} True if `value` is undefined. 511 */ 512 function isUndefined(value){return typeof value === 'undefined';} 513 514 515 /** 516 * @ngdoc function 517 * @name angular.isDefined 518 * @module ng 519 * @kind function 520 * 521 * @description 522 * Determines if a reference is defined. 523 * 524 * @param {*} value Reference to check. 525 * @returns {boolean} True if `value` is defined. 526 */ 527 function isDefined(value){return typeof value !== 'undefined';} 528 529 530 /** 531 * @ngdoc function 532 * @name angular.isObject 533 * @module ng 534 * @kind function 535 * 536 * @description 537 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not 538 * considered to be objects. Note that JavaScript arrays are objects. 539 * 540 * @param {*} value Reference to check. 541 * @returns {boolean} True if `value` is an `Object` but not `null`. 542 */ 543 function isObject(value){return value != null && typeof value === 'object';} 544 545 546 /** 547 * @ngdoc function 548 * @name angular.isString 549 * @module ng 550 * @kind function 551 * 552 * @description 553 * Determines if a reference is a `String`. 554 * 555 * @param {*} value Reference to check. 556 * @returns {boolean} True if `value` is a `String`. 557 */ 558 function isString(value){return typeof value === 'string';} 559 560 561 /** 562 * @ngdoc function 563 * @name angular.isNumber 564 * @module ng 565 * @kind function 566 * 567 * @description 568 * Determines if a reference is a `Number`. 569 * 570 * @param {*} value Reference to check. 571 * @returns {boolean} True if `value` is a `Number`. 572 */ 573 function isNumber(value){return typeof value === 'number';} 574 575 576 /** 577 * @ngdoc function 578 * @name angular.isDate 579 * @module ng 580 * @kind function 581 * 582 * @description 583 * Determines if a value is a date. 584 * 585 * @param {*} value Reference to check. 586 * @returns {boolean} True if `value` is a `Date`. 587 */ 588 function isDate(value) { 589 return toString.call(value) === '[object Date]'; 590 } 591 592 593 /** 594 * @ngdoc function 595 * @name angular.isArray 596 * @module ng 597 * @kind function 598 * 599 * @description 600 * Determines if a reference is an `Array`. 601 * 602 * @param {*} value Reference to check. 603 * @returns {boolean} True if `value` is an `Array`. 604 */ 605 var isArray = (function() { 606 if (!isFunction(Array.isArray)) { 607 return function(value) { 608 return toString.call(value) === '[object Array]'; 609 }; 610 } 611 return Array.isArray; 612 })(); 613 614 /** 615 * @ngdoc function 616 * @name angular.isFunction 617 * @module ng 618 * @kind function 619 * 620 * @description 621 * Determines if a reference is a `Function`. 622 * 623 * @param {*} value Reference to check. 624 * @returns {boolean} True if `value` is a `Function`. 625 */ 626 function isFunction(value){return typeof value === 'function';} 627 628 629 /** 630 * Determines if a value is a regular expression object. 631 * 632 * @private 633 * @param {*} value Reference to check. 634 * @returns {boolean} True if `value` is a `RegExp`. 635 */ 636 function isRegExp(value) { 637 return toString.call(value) === '[object RegExp]'; 638 } 639 640 641 /** 642 * Checks if `obj` is a window object. 643 * 644 * @private 645 * @param {*} obj Object to check 646 * @returns {boolean} True if `obj` is a window obj. 647 */ 648 function isWindow(obj) { 649 return obj && obj.window === obj; 650 } 651 652 653 function isScope(obj) { 654 return obj && obj.$evalAsync && obj.$watch; 655 } 656 657 658 function isFile(obj) { 659 return toString.call(obj) === '[object File]'; 660 } 661 662 663 function isBlob(obj) { 664 return toString.call(obj) === '[object Blob]'; 665 } 666 667 668 function isBoolean(value) { 669 return typeof value === 'boolean'; 670 } 671 672 673 var trim = (function() { 674 // native trim is way faster: http://jsperf.com/angular-trim-test 675 // but IE doesn't have it... :-( 676 // TODO: we should move this into IE/ES5 polyfill 677 if (!String.prototype.trim) { 678 return function(value) { 679 return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; 680 }; 681 } 682 return function(value) { 683 return isString(value) ? value.trim() : value; 684 }; 685 })(); 686 687 688 /** 689 * @ngdoc function 690 * @name angular.isElement 691 * @module ng 692 * @kind function 693 * 694 * @description 695 * Determines if a reference is a DOM element (or wrapped jQuery element). 696 * 697 * @param {*} value Reference to check. 698 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). 699 */ 700 function isElement(node) { 701 return !!(node && 702 (node.nodeName // we are a direct element 703 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API 704 } 705 706 /** 707 * @param str 'key1,key2,...' 708 * @returns {object} in the form of {key1:true, key2:true, ...} 709 */ 710 function makeMap(str) { 711 var obj = {}, items = str.split(","), i; 712 for ( i = 0; i < items.length; i++ ) 713 obj[ items[i] ] = true; 714 return obj; 715 } 716 717 718 if (msie < 9) { 719 nodeName_ = function(element) { 720 element = element.nodeName ? element : element[0]; 721 return lowercase( 722 (element.scopeName && element.scopeName != 'HTML') 723 ? element.scopeName + ':' + element.nodeName : element.nodeName 724 ); 725 }; 726 } else { 727 nodeName_ = function(element) { 728 return lowercase(element.nodeName ? element.nodeName : element[0].nodeName); 729 }; 730 } 731 732 733 function map(obj, iterator, context) { 734 var results = []; 735 forEach(obj, function(value, index, list) { 736 results.push(iterator.call(context, value, index, list)); 737 }); 738 return results; 739 } 740 741 742 /** 743 * @description 744 * Determines the number of elements in an array, the number of properties an object has, or 745 * the length of a string. 746 * 747 * Note: This function is used to augment the Object type in Angular expressions. See 748 * {@link angular.Object} for more information about Angular arrays. 749 * 750 * @param {Object|Array|string} obj Object, array, or string to inspect. 751 * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object 752 * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. 753 */ 754 function size(obj, ownPropsOnly) { 755 var count = 0, key; 756 757 if (isArray(obj) || isString(obj)) { 758 return obj.length; 759 } else if (isObject(obj)) { 760 for (key in obj) 761 if (!ownPropsOnly || obj.hasOwnProperty(key)) 762 count++; 763 } 764 765 return count; 766 } 767 768 769 function includes(array, obj) { 770 return indexOf(array, obj) != -1; 771 } 772 773 function indexOf(array, obj) { 774 if (array.indexOf) return array.indexOf(obj); 775 776 for (var i = 0; i < array.length; i++) { 777 if (obj === array[i]) return i; 778 } 779 return -1; 780 } 781 782 function arrayRemove(array, value) { 783 var index = indexOf(array, value); 784 if (index >=0) 785 array.splice(index, 1); 786 return value; 787 } 788 789 function isLeafNode (node) { 790 if (node) { 791 switch (nodeName_(node)) { 792 case "option": 793 case "pre": 794 case "title": 795 return true; 796 } 797 } 798 return false; 799 } 800 801 /** 802 * @ngdoc function 803 * @name angular.copy 804 * @module ng 805 * @kind function 806 * 807 * @description 808 * Creates a deep copy of `source`, which should be an object or an array. 809 *建立一個深拷貝的源,它能夠是一個對象或一個數組 810 * * If no destination is supplied, a copy of the object or array is created 811 * 若是未指定dest,對象或數組的一個副本會被建立 812 * * If a destination is provided, all of its elements (for array) or properties (for objects) 813 * are deleted and then all elements/properties from the source are copied to it. 814 * 若是指定了它的dest,則它的全部屬性或元素將會被刪除,而且它的全部元素或屬性將會從源裏面拷貝出來。 815 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. 816 * 若是源不是一個對象或數組,則操做會當即中止執行 817 * * If `source` is identical to 'destination' an exception will be thrown. 818 *若是源被標識爲一個dest,則會拋出一個異常 819 * @param {*} source The source that will be used to make a copy.//源,進行拷貝的參照對象,能夠是任意類型 820 * Can be any type, including primitives, `null`, and `undefined`. 821 * @param {(Object|Array)=} destination Destination into which the source is copied. If 822 * provided, must be of the same type as `source`.//參數爲一個對象或數組,若是被提供,則必須與源的類型相同 823 * @returns {*} The copy or updated `destination`, if `destination` was specified. 824 * 若是dest被指定的話,則更新dest;若是dest未被指定的話,是返回源的拷貝副本。 825 * 826 * @example 827 <example module="copyExample"> 828 <file name="index.html"> 829 <div ng-controller="ExampleController"> 830 <form novalidate class="simple-form"> 831 Name: <input type="text" ng-model="user.name" /><br /> 832 E-mail: <input type="email" ng-model="user.email" /><br /> 833 Gender: <input type="radio" ng-model="user.gender" value="male" />male 834 <input type="radio" ng-model="user.gender" value="female" />female<br /> 835 <button ng-click="reset()">RESET</button> 836 <button ng-click="update(user)">SAVE</button> 837 </form> 838 <pre>form = {{user | json}}</pre>//user是一個json對象 839 <pre>master = {{master | json}}</pre>master是一個json對象 840 </div> 841 842 <script> 843 angular.module('copyExample') 844 .controller('ExampleController', ['$scope', function($scope) {//$scope默認會被注入 845 $scope.master= {}; 846 847 $scope.update = function(user) { 848 // Example with 1 argument 849 $scope.master= angular.copy(user); 850 }; 851 852 $scope.reset = function() { 853 // Example with 2 arguments 854 angular.copy($scope.master, $scope.user); 855 }; 856 857 $scope.reset(); 858 }]); 859 </script> 860 </file> 861 </example> 862 */ 863 function copy(source, destination, stackSource, stackDest) { 864 if (isWindow(source) || isScope(source)) { 865 throw ngMinErr('cpws', 866 "Can't copy! Making copies of Window or Scope instances is not supported."); 867 } 868 869 if (!destination) {//若是指定dest 870 destination = source; 871 if (source) { 872 if (isArray(source)) { 873 destination = copy(source, [], stackSource, stackDest); 874 } else if (isDate(source)) { 875 destination = new Date(source.getTime()); 876 } else if (isRegExp(source)) { 877 destination = new RegExp(source.source); 878 } else if (isObject(source)) { 879 var emptyObject = Object.create(Object.getPrototypeOf(source)); 880 destination = copy(source, emptyObject, stackSource, stackDest); 881 } 882 } 883 } else {//若是不指定dest 884 if (source === destination) throw ngMinErr('cpi', 885 "Can't copy! Source and destination are identical."); 886 887 stackSource = stackSource || []; 888 stackDest = stackDest || []; 889 890 if (isObject(source)) { 891 var index = indexOf(stackSource, source); 892 if (index !== -1) return stackDest[index]; 893 894 stackSource.push(source); 895 stackDest.push(destination); 896 } 897 898 var result; 899 if (isArray(source)) { 900 destination.length = 0; 901 for ( var i = 0; i < source.length; i++) { 902 result = copy(source[i], null, stackSource, stackDest); 903 if (isObject(source[i])) { 904 stackSource.push(source[i]); 905 stackDest.push(result); 906 } 907 destination.push(result); 908 } 909 } else { 910 var h = destination.$$hashKey; 911 forEach(destination, function(value, key) { 912 delete destination[key]; 913 }); 914 for ( var key in source) { 915 if(source.hasOwnProperty(key)) { 916 result = copy(source[key], null, stackSource, stackDest); 917 if (isObject(source[key])) { 918 stackSource.push(source[key]); 919 stackDest.push(result); 920 } 921 destination[key] = result; 922 } 923 } 924 setHashKey(destination,h); 925 } 926 927 } 928 return destination; 929 } 930 931 /** 932 * Creates a shallow copy of an object, an array or a primitive 933 * 建立一個淺副本,能夠是一個數組或一個原始數據 934 */ 935 function shallowCopy(src, dst) { 936 var i = 0; 937 if (isArray(src)) { 938 dst = dst || []; 939 940 for (; i < src.length; i++) { 941 dst[i] = src[i]; 942 } 943 } else if (isObject(src)) { 944 dst = dst || {}; 945 946 var keys = Object.keys(src); 947 948 for (var l = keys.length; i < l; i++) { 949 var key = keys[i]; 950 951 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { 952 dst[key] = src[key]; 953 } 954 } 955 } 956 957 return dst || src; 958 } 959 960 961 /** 962 * @ngdoc function 963 * @name angular.equals 964 * @module ng 965 * @kind function 966 * 967 * @description 968 * Determines if two objects or two values are equivalent. Supports value types, regular 969 * expressions, arrays and objects. 970 * 971 * Two objects or values are considered equivalent if at least one of the following is true: 972 * 973 * * Both objects or values pass `===` comparison. 974 * * Both objects or values are of the same type and all of their properties are equal by 975 * comparing them with `angular.equals`. 976 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) 977 * * Both values represent the same regular expression (In JavaScript, 978 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual 979 * representation matches). 980 * 981 * During a property comparison, properties of `function` type and properties with names 982 * that begin with `$` are ignored. 983 * 984 * Scope and DOMWindow objects are being compared only by identify (`===`). 985 * 986 * @param {*} o1 Object or value to compare. 987 * @param {*} o2 Object or value to compare. 988 * @returns {boolean} True if arguments are equal. 989 */ 990 function equals(o1, o2) { 991 if (o1 === o2) return true; 992 if (o1 === null || o2 === null) return false; 993 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN 994 var t1 = typeof o1, t2 = typeof o2, length, key, keySet; 995 if (t1 == t2) { 996 if (t1 == 'object') { 997 if (isArray(o1)) { 998 if (!isArray(o2)) return false; 999 if ((length = o1.length) == o2.length) { 1000 for(key=0; key<length; key++) { 1001 if (!equals(o1[key], o2[key])) return false; 1002 } 1003 return true; 1004 } 1005 } else if (isDate(o1)) { 1006 return isDate(o2) && o1.getTime() == o2.getTime(); 1007 } else if (isRegExp(o1) && isRegExp(o2)) { 1008 return o1.toString() == o2.toString(); 1009 } else { 1010 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false; 1011 keySet = {}; 1012 for(key in o1) { 1013 if (key.charAt(0) === '$' || isFunction(o1[key])) continue; 1014 if (!equals(o1[key], o2[key])) return false; 1015 keySet[key] = true; 1016 } 1017 for(key in o2) { 1018 if (!keySet.hasOwnProperty(key) && 1019 key.charAt(0) !== '$' && 1020 o2[key] !== undefined && 1021 !isFunction(o2[key])) return false; 1022 } 1023 return true; 1024 } 1025 } 1026 } 1027 return false; 1028 } 1029 1030 1031 function csp() { 1032 return (document.securityPolicy && document.securityPolicy.isActive) || 1033 (document.querySelector && 1034 !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]'))); 1035 } 1036 1037 1038 function concat(array1, array2, index) { 1039 return array1.concat(slice.call(array2, index));//鏈接兩個數組,call(obj,具體的參數) 1040 } 1041 1042 //分割指定位置的數組數據 1043 function sliceArgs(args, startIndex) { 1044 return slice.call(args, startIndex || 0);//[].splice 1045 } 1046 1047 1048 /* jshint -W101 */ 1049 /** 1050 * @ngdoc function 1051 * @name angular.bind 1052 * @module ng 1053 * @kind function 1054 * 1055 * @description 1056 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for 1057 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also 1058 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as 1059 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). 1060 *當調用fn函數時,bn會被綁定到self,其中,self至關於this的做用。能夠指定args參數綁定到預先造成的函數中。 1061 * 返回一個函數的調用 1062 * @param {Object} self Context which `fn` should be evaluated in.//一個上下文對象 1063 * @param {function()} fn Function to be bound.//一個函數,它將會被綁定到self對象中 1064 * @param {...*} args Optional arguments to be prebound to the `fn` function call.//可選參數將會被綁定到fn函數中 1065 * @returns {function()} Function that wraps the `fn` with all the specified bindings. 1066 */ 1067 /* jshint +W101 */ 1068 function bind(self, fn) { 1069 //判斷可選參數是否被提供,若是被提供則爲sliceArg(argumentts,2),若是未被提供則爲空數組,[] 1070 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; 1071 if (isFunction(fn) && !(fn instanceof RegExp)) {//若是fn是一個函數 1072 return curryArgs.length//判斷可選參數是否被提供 1073 ? function() { 1074 return arguments.length 1075 ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))//若是可選參數被提供,則把可選參數綁定到self中 1076 : fn.apply(self, curryArgs);//若是可選參數未被提供,則返回self自己 1077 } 1078 : function() { 1079 return arguments.length 1080 ? fn.apply(self, arguments) 1081 : fn.call(self); 1082 }; 1083 } else { 1084 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) 1085 return fn;//在IE裏面沒有這些函數,因此綁定不了,直接返回fn函數 1086 } 1087 } 1088 1089 1090 function toJsonReplacer(key, value) { 1091 var val = value; 1092 1093 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { 1094 val = undefined; 1095 } else if (isWindow(value)) { 1096 val = '$WINDOW'; 1097 } else if (value && document === value) { 1098 val = '$DOCUMENT'; 1099 } else if (isScope(value)) { 1100 val = '$SCOPE'; 1101 } 1102 return val; 1103 } 1104 1105 1106 /** 1107 * @ngdoc function 1108 * @name angular.toJson 1109 * @module ng 1110 * @kind function 1111 * 1112 * @description 1113 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be 1114 * stripped since angular uses this notation internally. 1115 * 1116 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. 1117 * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. 1118 * @returns {string|undefined} JSON-ified string representing `obj`. 1119 */ 1120 //把指定的對象轉換成json字符串,並把json字符串裏面的值 替換成與之相對應的對象,如:$$scope,$widnow,$document 1121 function toJson(obj, pretty) { 1122 if (typeof obj === 'undefined') return undefined; 1123 return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); 1124 } 1125 1126 1127 /** 1128 * @ngdoc function 1129 * @name angular.fromJson 1130 * @module ng 1131 * @kind function 1132 * 1133 * @description 1134 * Deserializes a JSON string. 1135 * 1136 * @param {string} json JSON string to deserialize.//一個json字符串 1137 * @returns {Object|Array|string|number} Deserialized thingy.,返回一個數組,對象 1138 */ 1139 function fromJson(json) {// 1140 return isString(json) 1141 ? JSON.parse(json) 1142 : json; 1143 } 1144 1145 1146 /** 1147 * @returns {string} Returns the string representation of the element. 1148 */ 1149 function startingTag(element) {//查找angular的ng指令,如ngapp,ngrepeat,ngswitch etc. 1150 element = jqLite(element).clone();//克隆element元素 1151 try { 1152 // turns out IE does not let you set .html() on elements which 1153 // are not allowed to have children. So we just ignore it. 1154 element.empty();//IE不容許用設置.html()設置元素,也就是說不容許有子元素,所以置爲空 1155 } catch(e) {} 1156 // As Per DOM Standards 1157 var TEXT_NODE = 3;//標準的文節點 1158 var elemHtml = jqLite('<div>').append(element).html();//把div標籤添加到新克隆出來的element上,並獲取如今的值 1159 try { 1160 return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : 1161 elemHtml. 1162 match(/^(<[^>]+>)/)[1].//正則表達式表示的示例內容如,<div ng-app='myModule'> 1163 //正則:/^<([\w\-]+)/,表示的示例內容,如:<div ng-app='myModule'>, 1164 // 正則:([\w\-]+),表示的示例內容如,ng-app,由此看來,angular在html裏面寫的內容必須是:標準的Html文內容 1165 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });//<ng-app 1166 } catch(e) { 1167 return lowercase(elemHtml);//若是不是上面的狀況,慢把新取出的值直接返回 1168 } 1169 1170 } 1171 1172 1173 ///////////////////////////////////////////////// 1174 1175 /** 1176 * Tries to decode the URI component without throwing an exception. 1177 *調試解碼URI組件,不拋出異常 1178 * @private 爲私有方法 1179 * @param str value potential URI component to check.//str是URI組件須要檢查的內容 1180 * @returns {boolean} True if `value` can be decoded,若是str被正常的解析,則返回true 1181 * with the decodeURIComponent function. 1182 */ 1183 function tryDecodeURIComponent(value) { 1184 try { 1185 return decodeURIComponent(value); 1186 } catch(e) { 1187 // Ignore any invalid uri component//忽略任何無效的URI組件內容 1188 } 1189 } 1190 1191 1192 /** 1193 * Parses an escaped url query string into key-value pairs.// 1194 * 解析一個轉義的url查詢字符串爲鍵值對形式 1195 * @returns {Object.<string,boolean|Array>}//返回Object.字符串/boolean,數組 1196 */ 1197 function parseKeyValue(/**string*/keyValue) { 1198 var obj = {}, key_value, key; 1199 //keyvalue的內容示例如,http://www.baidu.com?search=angular&date=20141215,結果爲:search:angular,date:20141215 1200 forEach((keyValue || "").split('&'), function(keyValue) { 1201 if ( keyValue ) { 1202 key_value = keyValue.split('='); 1203 key = tryDecodeURIComponent(key_value[0]); 1204 if ( isDefined(key) ) { 1205 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; 1206 if (!hasOwnProperty.call(obj, key)) { 1207 obj[key] = val; 1208 } else if(isArray(obj[key])) { 1209 obj[key].push(val); 1210 } else { 1211 obj[key] = [obj[key],val]; 1212 } 1213 } 1214 } 1215 }); 1216 return obj; 1217 }
續:時間:2014年12月15日 15:36:44css
1 function toKeyValue(obj) { 2 var parts = []; 3 forEach(obj, function(value, key) { 4 if (isArray(value)) { 5 forEach(value, function(arrayValue) { 6 parts.push(encodeUriQuery(key, true) + 7 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); 8 }); 9 } else { 10 parts.push(encodeUriQuery(key, true) + 11 (value === true ? '' : '=' + encodeUriQuery(value, true))); 12 } 13 }); 14 return parts.length ? parts.join('&') : ''; 15 } 16 17 18 /** 19 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow 20 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 21 * segments: 22 * segment = *pchar 23 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 24 * pct-encoded = "%" HEXDIG HEXDIG 25 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 26 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 27 * / "*" / "+" / "," / ";" / "=" 28 */ 29 function encodeUriSegment(val) { 30 return encodeUriQuery(val, true). 31 replace(/%26/gi, '&'). 32 replace(/%3D/gi, '='). 33 replace(/%2B/gi, '+'); 34 } 35 36 37 /** 38 * This method is intended for encoding *key* or *value* parts of query component. We need a custom 39 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be 40 * encoded per http://tools.ietf.org/html/rfc3986: 41 * query = *( pchar / "/" / "?" ) 42 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"//pchar爲字符串的分割符,對應下面的三種狀況 43 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 44 * pct-encoded = "%" HEXDIG HEXDIG 45 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"// 46 * / "*" / "+" / "," / ";" / "=" 47 */ 48 function encodeUriQuery(val, pctEncodeSpaces) { 49 return encodeURIComponent(val). 50 replace(/%40/gi, '@'). 51 replace(/%3A/gi, ':'). 52 replace(/%24/g, '$'). 53 replace(/%2C/gi, ','). 54 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); 55 } 56 57 //定義angular屬性的前綴,如ng-,data-ng,ng:-,x-ng- 58 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; 59 60 //參數示例:ng-repeat,repeat 61 function getNgAttribute(element, ngAttr) {//獲取angular的*ng-屬性 62 var attr, i, ii = ngAttrPrefixes.length, j, jj; 63 element = jqLite(element);//獲取傳進來的element元素 64 for (i=0; i<ii; ++i) { 65 attr = ngAttrPrefixes[i] + ngAttr;//顯示的示例代碼,如:ng-app,ng-repeat 66 if (isString(attr = element.attr(attr))) {//若是找到則當即返回,找到的內容 67 return attr; 68 } 69 } 70 return null;//若是找不到則返回爲空 71 } 72 73 /** 74 * @ngdoc directive 75 * @name ngApp 76 * @module ng 77 * 78 * @element ANY //能夠是任意內容 79 * @param {angular.Module} ngApp an optional application//angular.module,是一個可選的 80 * {@link angular.module module} name to load.//按照模塊的名稱載入的,如angular.module('myModule',["book-store","book"]);,其中,myModule就是模塊名 81 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be 82 * created in "strict-di" mode. This means that the application will fail to invoke functions which 83 * do not use explicit function annotation (and are thus unsuitable for minification), as described 84 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in 85 * tracking down the root of these bugs. 86 * 87 * @description 88 * 89 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive 90 * 用這個指令能夠自動的去啓動angular的應用程序, 91 * designates the **root element** of the application and is typically placed near the root element 92 * ngApp做爲angular應用程序的根元素,它是一個典型的點位符,如<html><body><head><div> etc. 93 * of the page - e.g. on the `<body>` or `<html>` tags. 94 * 95 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp 96 * 在每個html文檔中,僅只有一個angular應用程序能夠被自動啓動 97 * found in the document will be used to define the root element to auto-bootstrap as an 98 * 在文檔中發現的第一個ngApp被視爲能夠自動啓動的應用程序的根元素 99 * application. To run multiple applications in an HTML document you must manually bootstrap them using 100 * 在一個html文檔中運行多個應用程序,則須要手動的啓動他們。 101 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. 102 *angular應用程序不可以互相嵌套 103 * You can specify an **AngularJS module** to be used as the root module for the application. This 104 * 你能夠指定一個angular的模板,做爲應用程序的頂級模塊 105 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and 106 * should contain the application code needed or have dependencies on other modules that will 107 * contain the code. See {@link angular.module} for more information. 108 *當應用程序啓動而且包含有它所依賴的其餘模塊代碼時,上面所指定的模塊能夠被注入到應用程序當中 109 * In the example below if the `ngApp` directive were not placed on the `html` element then the 110 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` 111 * would not be resolved to `3`. 112 *在下面的示例中,若是ngApp在Html頁面中找不到或不存在,則這個Html文檔不可以被正常的編譯,AppController這個控制器也不可以被正常的實例化 113 * 解析出來的{{a+b}}的結果也不會爲3 114 * `ngApp` is the easiest, and most common, way to bootstrap an application. 115 *ngApp是用來 啓動一個應用程序最簡單,最經常使用 的指令, 116 <example module="ngAppDemo"> 117 <file name="index.html"> 118 <div ng-controller="ngAppDemoController"> 119 I can add: {{a}} + {{b}} = {{ a+b }} 120 </div> 121 </file> 122 <file name="script.js"> 123 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { 124 $scope.a = 1; 125 $scope.b = 2; 126 }); 127 </file> 128 </example> 129 * 130 * Using `ngStrictDi`, you would see something like this://啓動嚴格模式 131 * 132 <example ng-app-included="true"> 133 <file name="index.html"> 134 <div ng-app="ngAppStrictDemo" ng-strict-di>//啓動嚴格模式 135 <div ng-controller="GoodController1"> 136 I can add: {{a}} + {{b}} = {{ a+b }} 137 138 <p>This renders because the controller does not fail to 139 instantiate, by using explicit annotation style (see 140 script.js for details) 141 </p> 142 </div> 143 144 <div ng-controller="GoodController2"> 145 Name: <input ng-model="name"><br /> 146 Hello, {{name}}! 147 148 <p>This renders because the controller does not fail to 149 instantiate, by using explicit annotation style 150 (see script.js for details) 151 </p> 152 </div> 153 154 <div ng-controller="BadController"> 155 I can add: {{a}} + {{b}} = {{ a+b }} 156 157 <p>The controller could not be instantiated, due to relying 158 on automatic function annotations (which are disabled in 159 strict mode). As such, the content of this section is not 160 interpolated, and there should be an error in your web console. 161 </p> 162 </div> 163 </div> 164 </file> 165 <file name="script.js"> 166 angular.module('ngAppStrictDemo', []) 167 // BadController will fail to instantiate, due to relying on automatic function annotation, 168 // rather than an explicit annotation 169 .controller('BadController', function($scope) { 170 $scope.a = 1; 171 $scope.b = 2; 172 }) 173 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, 174 // due to using explicit annotations using the array style and $inject property, respectively. 175 .controller('GoodController1', ['$scope', function($scope) { 176 $scope.a = 1; 177 $scope.b = 2; 178 }]) 179 .controller('GoodController2', GoodController2); 180 function GoodController2($scope) { 181 $scope.name = "World"; 182 } 183 GoodController2.$inject = ['$scope']; 184 </file> 185 <file name="style.css"> 186 div[ng-controller] { 187 margin-bottom: 1em; 188 -webkit-border-radius: 4px; 189 border-radius: 4px; 190 border: 1px solid; 191 padding: .5em; 192 } 193 div[ng-controller^=Good] { 194 border-color: #d6e9c6; 195 background-color: #dff0d8; 196 color: #3c763d; 197 } 198 div[ng-controller^=Bad] { 199 border-color: #ebccd1; 200 background-color: #f2dede; 201 color: #a94442; 202 margin-bottom: 0; 203 } 204 </file> 205 </example> 206 */ 207 function angularInit(element, bootstrap) {//type:any,[] 208 var elements = [element], 209 appElement, 210 module, 211 config = {}, 212 names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],//angular所能識別的ngApp指令 213 options = { 214 'boolean': ['strict-di'] 215 }, 216 NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;//*ng-指令的正則表達式,如:ng-app:red 217 218 function append(element) {//內部函數 219 element && elements.push(element); 220 } 221 222 forEach(names, function(name) { 223 names[name] = true; 224 append(document.getElementById(name)); 225 name = name.replace(':', '\\:');//示例內容,如:ng:app轉換爲,ng\:app 226 /* 227 * //獲取<div>中的全部圖像(和getElementsByTaName("img")同樣) 228 var images = document.getElementById("myDiv").querySelectorAll("img"); 229 230 //獲取全部包含「selected」類的元素 231 var selected = document.querySelectorall(".selected"); 232 233 //獲取全部<p>元素中的<strong>元素 234 var strongs = document.querySelectorAll("p strong"); 235 * */ 236 if (element.querySelectorAll) {//只要調用querySelectorAll()都會返回一個StaticNodeList對象無論匹配的元素有幾個;若是沒有匹配,那麼StaticNodeList爲空。 237 forEach(element.querySelectorAll('.' + name), append);//.ng-App 238 forEach(element.querySelectorAll('.' + name + '\\:'), append);//.ng\:App 239 forEach(element.querySelectorAll('[' + name + ']'), append);//[ng-App] 240 } 241 }); 242 243 forEach(elements, function(element) { 244 if (!appElement) { 245 var className = ' ' + element.className + ' '; 246 var match = NG_APP_CLASS_REGEXP.exec(className);//判斷是否匹配*ng-指令的正則表達式,如ng-app:red 247 if (match) { 248 appElement = element; 249 module = (match[2] || '').replace(/\s+/g, ','); 250 } else { 251 forEach(element.attributes, function(attr) { 252 if (!appElement && names[attr.name]) { 253 appElement = element; 254 module = attr.value; 255 } 256 }); 257 } 258 } 259 }); 260 if (appElement) { 261 config.strictDi = getNgAttribute(appElement, "strict-di") !== null; 262 bootstrap(appElement, module ? [module] : [], config);//示例代碼,如:angular.module(document,[],cofnig);//是一個回調函數 263 } 264 } 265 266 /** 267 * @ngdoc function 268 * @name angular.bootstrap 269 * @module ng 270 * @description 271 * Use this function to manually start up angular application. 272 *用這個函數手動啓動angular應用程序 273 * See: {@link guide/bootstrap Bootstrap} 274 * 275 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually. 276 * They must use {@link ng.directive:ngApp ngApp}. 277 *注意:protractor是基於end-toend來測試的,所以不可以使用這個函數來手動啓動應用程序 278 * Angular will detect if it has been loaded into the browser more than once and only allow the 279 * first loaded script to be bootstrapped and will report a warning to the browser console for 280 * each of the subsequent scripts. This prevents strange results in applications, where otherwise 281 * multiple instances of Angular try to work on the DOM. 282 * 283 * ```html 284 * <!doctype html> 285 * <html> 286 * <body> 287 * <div ng-controller="WelcomeController"> 288 * {{greeting}} 289 * </div> 290 * 291 * <script src="angular.js"></script> 292 * <script> 293 * var app = angular.module('demo', []) 294 * .controller('WelcomeController', function($scope) { 295 * $scope.greeting = 'Welcome!'; 296 * }); 297 * angular.bootstrap(document, ['demo']);//document,[],config 298 * </script> 299 * </body> 300 * </html> 301 * ``` 302 * 303 * @param {DOMElement} element DOM element which is the root of angular application.//dom元素,angular應用程序的根元素 304 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.//被加載到應用程序中的依賴模塊 305 * Each item in the array should be the name of a predefined module or a (DI annotated)//在數組中的每一項,應該被預先定義或注入 306 * function that will be invoked by the injector as a run block. 307 * See: {@link angular.module modules} 308 * @param {Object=} config an object for defining configuration options for the application. The 309 * following keys are supported: 310 * 311 * - `strictDi`: disable automatic function annotation for the application. This is meant to 312 * assist in finding bugs which break minified code. 313 * 314 * @returns {auto.$injector} Returns the newly created injector for this app. 315 * 返回新這個應用程序新建立的注入器 316 */ 317 function bootstrap(element, modules, config) { 318 if (!isObject(config)) config = {}; 319 var defaultConfig = { 320 strictDi: false 321 }; 322 /** 323 * function extend(dst) {//這裏實現config繼承 324 var h = dst.$$hashKey; 325 forEach(arguments, function(obj) { 326 if (obj !== dst) { 327 forEach(obj, function(value, key) { 328 dst[key] = value; 329 }); 330 } 331 }); 332 333 setHashKey(dst,h); 334 return dst; 335 } 336 */ 337 config = extend(defaultConfig, config);//這裏實現config繼承 338 var doBootstrap = function() {//返回新建立的注入器 339 element = jqLite(element); 340 341 if (element.injector()) {//App Already Bootstrapped with this Element 342 var tag = (element[0] === document) ? 'document' : startingTag(element); 343 throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag); 344 } 345 346 modules = modules || [];//element所依賴的模塊數組 347 modules.unshift(['$provide', function($provide) { 348 $provide.value('$rootElement', element);//注入根元素,如:document,html,ng-app 349 }]); 350 modules.unshift('ng');//添加anggular的前綴,ng 351 var injector = createInjector(modules, config.strictDi);//爲模塊數組建立一個注入器 352 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',//用新建立的注入器注入默認的對象,如:$rootScope,$rootEolement,$compile,$injector 353 function(scope, element, compile, injector) {//這個函數的參數與injector.invoke裏面的第一個參數所對應 354 scope.$apply(function() { 355 element.data('$injector', injector); 356 compile(element)(scope); 357 }); 358 }] 359 ); 360 return injector;//返回新建立的注入器 361 }; 362 363 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;//angular延遲啓動 364 365 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { 366 return doBootstrap(); 367 } 368 369 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); 370 angular.resumeBootstrap = function(extraModules) {//從新激活angular應用程序 371 forEach(extraModules, function(module) { 372 modules.push(module); 373 }); 374 doBootstrap(); 375 }; 376 } 377 378 var SNAKE_CASE_REGEXP = /[A-Z]/g; 379 function snake_case(name, separator) { 380 separator = separator || '_'; 381 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { 382 return (pos ? separator : '') + letter.toLowerCase(); 383 }); 384 } 385 386 function bindJQuery() { 387 var originalCleanData; 388 // bind to jQuery if present; 389 jQuery = window.jQuery; 390 // Use jQuery if it exists with proper functionality, otherwise default to us. 391 // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support. 392 if (jQuery && jQuery.fn.on) {//判斷是否已經引入其餘版本的JQuery,若是是則angular使用的是外部的jQuery 393 jqLite = jQuery; 394 extend(jQuery.fn, { 395 scope: JQLitePrototype.scope, 396 isolateScope: JQLitePrototype.isolateScope, 397 controller: JQLitePrototype.controller, 398 injector: JQLitePrototype.injector, 399 inheritedData: JQLitePrototype.inheritedData 400 }); 401 402 originalCleanData = jQuery.cleanData; 403 // Prevent double-proxying. 404 originalCleanData = originalCleanData.$$original || originalCleanData; 405 406 // All nodes removed from the DOM via various jQuery APIs like .remove() 407 // are passed through jQuery.cleanData. Monkey-patch this method to fire 408 // the $destroy event on all removed nodes. 409 jQuery.cleanData = function(elems) { 410 for (var i = 0, elem; (elem = elems[i]) != null; i++) { 411 jQuery(elem).triggerHandler('$destroy'); 412 } 413 originalCleanData(elems); 414 }; 415 jQuery.cleanData.$$original = originalCleanData; 416 } else {//若是沒有引入外部的jQuery,則angular使用其內容的jQlite 417 jqLite = JQLite; 418 } 419 420 angular.element = jqLite;//把jqLit綁定到angular中 421 } 422 423 /** 424 * throw error if the argument is falsy. 425 */ 426 function assertArg(arg, name, reason) { 427 if (!arg) { 428 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); 429 } 430 return arg; 431 } 432 433 function assertArgFn(arg, name, acceptArrayAnnotation) { 434 if (acceptArrayAnnotation && isArray(arg)) { 435 arg = arg[arg.length - 1]; 436 } 437 438 assertArg(isFunction(arg), name, 'not a function, got ' + 439 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); 440 return arg; 441 } 442 443 /** 444 * throw error if the name given is hasOwnProperty 445 * @param {String} name the name to test 446 * @param {String} context the context in which the name is used, such as module or directive 447 */ 448 function assertNotHasOwnProperty(name, context) { 449 if (name === 'hasOwnProperty') { 450 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); 451 } 452 } 453 454 /** 455 * Return the value accessible from the object by path. Any undefined traversals are ignored 456 * @param {Object} obj starting object 457 * @param {String} path path to traverse 458 * @param {boolean} [bindFnToScope=true] 459 * @returns {Object} value as accessible by path 460 */ 461 //TODO(misko): this function needs to be removed 462 function getter(obj, path, bindFnToScope) { 463 if (!path) return obj; 464 var keys = path.split('.'); 465 var key; 466 var lastInstance = obj; 467 var len = keys.length; 468 469 for (var i = 0; i < len; i++) { 470 key = keys[i]; 471 if (obj) { 472 obj = (lastInstance = obj)[key]; 473 } 474 } 475 if (!bindFnToScope && isFunction(obj)) { 476 return bind(lastInstance, obj); 477 } 478 return obj; 479 } 480 481 /** 482 * Return the DOM siblings between the first and last node in the given array. 483 * @param {Array} array like object 484 * @returns {DOMElement} object containing the elements 485 */ 486 function getBlockElements(nodes) { 487 var startNode = nodes[0], 488 endNode = nodes[nodes.length - 1]; 489 if (startNode === endNode) { 490 return jqLite(startNode); 491 } 492 493 var element = startNode; 494 var elements = [element]; 495 496 do { 497 element = element.nextSibling; 498 if (!element) break; 499 elements.push(element); 500 } while (element !== endNode); 501 502 return jqLite(elements); 503 } 504 505 /** 506 * @ngdoc type 507 * @name angular.Module 508 * @module ng 509 * @description 510 * 511 * Interface for configuring angular {@link angular.module modules}. 512 */ 513 514 function setupModuleLoader(window) { 515 516 var $injectorMinErr = minErr('$injector'); 517 var ngMinErr = minErr('ng'); 518 519 function ensure(obj, name, factory) { 520 return obj[name] || (obj[name] = factory()); 521 } 522 523 var angular = ensure(window, 'angular', Object); 524 525 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap 526 angular.$$minErr = angular.$$minErr || minErr; 527 528 return ensure(angular, 'module', function() { 529 /** @type {Object.<string, angular.Module>} */ 530 var modules = {}; 531 532 /** 533 * @ngdoc function 534 * @name angular.module 535 * @module ng 536 * @description 537 * 538 * The `angular.module` is a global place for creating, registering and retrieving Angular 539 * modules. 540 * All modules (angular core or 3rd party) that should be available to an application must be 541 * registered using this mechanism. 542 * 543 * When passed two or more arguments, a new module is created. If passed only one argument, an 544 * existing module (the name passed as the first argument to `module`) is retrieved. 545 * 546 * 547 * # Module 548 * 549 * A module is a collection of services, directives, controllers, filters, and configuration information. 550 * `angular.module` is used to configure the {@link auto.$injector $injector}. 551 * 552 * ```js 553 * // Create a new module 554 * var myModule = angular.module('myModule', []); 555 * 556 * // register a new service 557 * myModule.value('appName', 'MyCoolApp'); 558 * 559 * // configure existing services inside initialization blocks. 560 * myModule.config(['$locationProvider', function($locationProvider) { 561 * // Configure existing providers 562 * $locationProvider.hashPrefix('!'); 563 * }]); 564 * ``` 565 * 566 * Then you can create an injector and load your modules like this: 567 * 568 * ```js 569 * var injector = angular.injector(['ng', 'myModule']) 570 * ``` 571 * 572 * However it's more likely that you'll just use 573 * {@link ng.directive:ngApp ngApp} or 574 * {@link angular.bootstrap} to simplify this process for you. 575 * 576 * @param {!string} name The name of the module to create or retrieve. 577 * @param {!Array.<string>=} requires If specified then new module is being created. If 578 * unspecified then the module is being retrieved for further configuration. 579 * @param {Function=} configFn Optional configuration function for the module. Same as 580 * {@link angular.Module#config Module#config()}. 581 * @returns {module} new module with the {@link angular.Module} api. 582 */ 583 return function module(name, requires, configFn) {//var myModule = angular.module('myModule', []); 584 var assertNotHasOwnProperty = function(name, context) { 585 if (name === 'hasOwnProperty') { 586 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); 587 } 588 }; 589 590 assertNotHasOwnProperty(name, 'module'); 591 if (requires && modules.hasOwnProperty(name)) { 592 modules[name] = null; 593 } 594 return ensure(modules, name, function() { 595 if (!requires) { 596 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + 597 "the module name or forgot to load it. If registering a module ensure that you " + 598 "specify the dependencies as the second argument.", name); 599 } 600 601 /** @type {!Array.<Array.<*>>} */ 602 var invokeQueue = []; 603 604 /** @type {!Array.<Function>} */ 605 var configBlocks = []; 606 607 /** @type {!Array.<Function>} */ 608 var runBlocks = []; 609 610 var config = invokeLater('$injector', 'invoke', 'push', configBlocks); 611 612 /** @type {angular.Module} */ 613 var moduleInstance = { 614 // Private state 615 _invokeQueue: invokeQueue, 616 _configBlocks: configBlocks, 617 _runBlocks: runBlocks, 618 619 /** 620 * @ngdoc property 621 * @name angular.Module#requires 622 * @module ng 623 * @returns {Array.<string>} List of module names which must be loaded before this module. 624 * @description 625 * Holds the list of modules which the injector will load before the current module is 626 * loaded. 627 */ 628 requires: requires, 629 630 /** 631 * @ngdoc property 632 * @name angular.Module#name 633 * @module ng 634 * @returns {string} Name of the module. 635 * @description 636 */ 637 name: name, 638 639 640 /** 641 * @ngdoc method 642 * @name angular.Module#provider 643 * @module ng 644 * @param {string} name service name 645 * @param {Function} providerType Construction function for creating new instance of the 646 * service. 647 * @description 648 * See {@link auto.$provide#provider $provide.provider()}. 649 */ 650 provider: invokeLater('$provide', 'provider'), 651 652 /** 653 * @ngdoc method 654 * @name angular.Module#factory 655 * @module ng 656 * @param {string} name service name 657 * @param {Function} providerFunction Function for creating new instance of the service. 658 * @description 659 * See {@link auto.$provide#factory $provide.factory()}. 660 */ 661 factory: invokeLater('$provide', 'factory'), 662 663 /** 664 * @ngdoc method 665 * @name angular.Module#service 666 * @module ng 667 * @param {string} name service name 668 * @param {Function} constructor A constructor function that will be instantiated. 669 * @description 670 * See {@link auto.$provide#service $provide.service()}. 671 */ 672 service: invokeLater('$provide', 'service'), 673 674 /** 675 * @ngdoc method 676 * @name angular.Module#value 677 * @module ng 678 * @param {string} name service name 679 * @param {*} object Service instance object. 680 * @description 681 * See {@link auto.$provide#value $provide.value()}. 682 */ 683 value: invokeLater('$provide', 'value'), 684 685 /** 686 * @ngdoc method 687 * @name angular.Module#constant 688 * @module ng 689 * @param {string} name constant name 690 * @param {*} object Constant value. 691 * @description 692 * Because the constant are fixed, they get applied before other provide methods. 693 * See {@link auto.$provide#constant $provide.constant()}. 694 */ 695 constant: invokeLater('$provide', 'constant', 'unshift'), 696 697 /** 698 * @ngdoc method 699 * @name angular.Module#animation 700 * @module ng 701 * @param {string} name animation name 702 * @param {Function} animationFactory Factory function for creating new instance of an 703 * animation. 704 * @description 705 * 706 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. 707 * 708 * 709 * Defines an animation hook that can be later used with 710 * {@link ngAnimate.$animate $animate} service and directives that use this service. 711 * 712 * ```js 713 * module.animation('.animation-name', function($inject1, $inject2) { 714 * return { 715 * eventName : function(element, done) { 716 * //code to run the animation 717 * //once complete, then run done() 718 * return function cancellationFunction(element) { 719 * //code to cancel the animation 720 * } 721 * } 722 * } 723 * }) 724 * ``` 725 * 726 * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and 727 * {@link ngAnimate ngAnimate module} for more information. 728 */ 729 animation: invokeLater('$animateProvider', 'register'), 730 731 /** 732 * @ngdoc method 733 * @name angular.Module#filter 734 * @module ng 735 * @param {string} name Filter name. 736 * @param {Function} filterFactory Factory function for creating new instance of filter. 737 * @description 738 * See {@link ng.$filterProvider#register $filterProvider.register()}. 739 */ 740 filter: invokeLater('$filterProvider', 'register'), 741 742 /** 743 * @ngdoc method 744 * @name angular.Module#controller 745 * @module ng 746 * @param {string|Object} name Controller name, or an object map of controllers where the 747 * keys are the names and the values are the constructors. 748 * @param {Function} constructor Controller constructor function. 749 * @description 750 * See {@link ng.$controllerProvider#register $controllerProvider.register()}. 751 */ 752 controller: invokeLater('$controllerProvider', 'register'), 753 754 /** 755 * @ngdoc method 756 * @name angular.Module#directive 757 * @module ng 758 * @param {string|Object} name Directive name, or an object map of directives where the 759 * keys are the names and the values are the factories. 760 * @param {Function} directiveFactory Factory function for creating new instance of 761 * directives. 762 * @description 763 * See {@link ng.$compileProvider#directive $compileProvider.directive()}. 764 */ 765 directive: invokeLater('$compileProvider', 'directive'), 766 767 /** 768 * @ngdoc method 769 * @name angular.Module#config 770 * @module ng 771 * @param {Function} configFn Execute this function on module load. Useful for service 772 * configuration. 773 * @description 774 * Use this method to register work which needs to be performed on module loading. 775 * For more about how to configure services, see 776 * {@link providers#providers_provider-recipe Provider Recipe}. 777 */ 778 config: config, 779 780 /** 781 * @ngdoc method 782 * @name angular.Module#run 783 * @module ng 784 * @param {Function} initializationFn Execute this function after injector creation. 785 * Useful for application initialization. 786 * @description 787 * Use this method to register work which should be performed when the injector is done 788 * loading all modules. 789 */ 790 run: function(block) { 791 runBlocks.push(block); 792 return this; 793 } 794 }; 795 796 if (configFn) { 797 config(configFn); 798 } 799 800 return moduleInstance; 801 802 /** 803 * @param {string} provider 804 * @param {string} method 805 * @param {String=} insertMethod 806 * @returns {angular.Module} 807 */ 808 function invokeLater(provider, method, insertMethod, queue) { 809 if (!queue) queue = invokeQueue; 810 return function() { 811 queue[insertMethod || 'push']([provider, method, arguments]); 812 return moduleInstance; 813 }; 814 } 815 }); 816 }; 817 }); 818 819 } 820 821 /* global angularModule: true,//angular全局模塊 822 version: true, 823 824 $LocaleProvider,//local 825 $CompileProvider,//compile 826 827 htmlAnchorDirective,//<a href="http://www.baidu.com">baidu</a> 828 inputDirective,//<input type='text' name='name'> 829 inputDirective,//<input type='text' name='pwd'> 830 formDirective,//<form action ='localhost:8080/book/booklist/' method='post'> 831 scriptDirective,//<script src='angular.min.js'></script> 832 selectDirective,//<select name='city' ></select> 833 styleDirective,//<style></style> 834 optionDirective,<option></option> 835 ngBindDirective,<div ng-bind='{{bookPrice}}'></div> 836 ngBindHtmlDirective, 837 ngBindTemplateDirective, 838 ngClassDirective, 839 ngClassEvenDirective, 840 ngClassOddDirective, 841 ngCspDirective, 842 ngCloakDirective, 843 ngControllerDirective, 844 ngFormDirective, 845 ngHideDirective, 846 ngIfDirective, 847 ngIncludeDirective, 848 ngIncludeFillContentDirective, 849 ngInitDirective, 850 ngNonBindableDirective, 851 ngPluralizeDirective, 852 ngRepeatDirective, 853 ngShowDirective, 854 ngStyleDirective, 855 ngSwitchDirective, 856 ngSwitchWhenDirective, 857 ngSwitchDefaultDirective, 858 ngOptionsDirective, 859 ngTranscludeDirective, 860 ngModelDirective, 861 ngListDirective, 862 ngChangeDirective, 863 patternDirective, 864 patternDirective, 865 requiredDirective, 866 requiredDirective, 867 minlengthDirective, 868 minlengthDirective, 869 maxlengthDirective, 870 maxlengthDirective, 871 ngValueDirective, 872 ngModelOptionsDirective, 873 ngAttributeAliasDirectives, 874 ngEventDirectives, 875 876 $AnchorScrollProvider, 877 $AnimateProvider, 878 $BrowserProvider, 879 $CacheFactoryProvider, 880 $ControllerProvider, 881 $DocumentProvider, 882 $ExceptionHandlerProvider, 883 $FilterProvider, 884 $InterpolateProvider, 885 $IntervalProvider, 886 $HttpProvider, 887 $HttpBackendProvider, 888 $LocationProvider, 889 $LogProvider, 890 $ParseProvider, 891 $RootScopeProvider, 892 $QProvider, 893 $$QProvider, 894 $$SanitizeUriProvider, 895 $SceProvider, 896 $SceDelegateProvider, 897 $SnifferProvider, 898 $TemplateCacheProvider, 899 $TimeoutProvider, 900 $$RAFProvider, 901 $$AsyncCallbackProvider, 902 $WindowProvider 903 */ 904 905 906 /** 907 * @ngdoc object 908 * @name angular.version 909 * @module ng 910 * @description 911 * An object that contains information about the current AngularJS version. This object has the 912 * following properties: 913 * 914 * - `full` – `{string}` – Full version string, such as "0.9.18". 915 * - `major` – `{number}` – Major version number, such as "0". 916 * - `minor` – `{number}` – Minor version number, such as "9". 917 * - `dot` – `{number}` – Dot version number, such as "18". 918 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". 919 */ 920 var version = { 921 full: '1.3.0-beta.15', // all of these placeholder strings will be replaced by grunt's 922 major: 1, // package task 923 minor: 3, 924 dot: 0, 925 codeName: 'unbelievable-advancement' 926 };
續:時間:2014年12月15日 17:30:46html
1 function publishExternalAPI(angular){ 2 extend(angular, { 3 'bootstrap': bootstrap, 4 'copy': copy, 5 'extend': extend, 6 'equals': equals, 7 'element': jqLite, 8 'forEach': forEach, 9 'injector': createInjector, 10 'noop':noop, 11 'bind':bind, 12 'toJson': toJson, 13 'fromJson': fromJson, 14 'identity':identity, 15 'isUndefined': isUndefined, 16 'isDefined': isDefined, 17 'isString': isString, 18 'isFunction': isFunction, 19 'isObject': isObject, 20 'isNumber': isNumber, 21 'isElement': isElement, 22 'isArray': isArray, 23 'version': version, 24 'isDate': isDate, 25 'lowercase': lowercase, 26 'uppercase': uppercase, 27 'callbacks': {counter: 0}, 28 '$$minErr': minErr, 29 '$$csp': csp 30 }); 31 32 angularModule = setupModuleLoader(window); 33 try { 34 angularModule('ngLocale'); 35 } catch (e) { 36 angularModule('ngLocale', []).provider('$locale', $LocaleProvider); 37 } 38 39 angularModule('ng', ['ngLocale'], ['$provide', 40 function ngModule($provide) { 41 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. 42 $provide.provider({ 43 $$sanitizeUri: $$SanitizeUriProvider 44 }); 45 $provide.provider('$compile', $CompileProvider). 46 directive({ 47 a: htmlAnchorDirective, 48 input: inputDirective, 49 textarea: inputDirective, 50 form: formDirective, 51 script: scriptDirective, 52 select: selectDirective, 53 style: styleDirective, 54 option: optionDirective, 55 ngBind: ngBindDirective, 56 ngBindHtml: ngBindHtmlDirective, 57 ngBindTemplate: ngBindTemplateDirective, 58 ngClass: ngClassDirective, 59 ngClassEven: ngClassEvenDirective, 60 ngClassOdd: ngClassOddDirective, 61 ngCloak: ngCloakDirective, 62 ngController: ngControllerDirective, 63 ngForm: ngFormDirective, 64 ngHide: ngHideDirective, 65 ngIf: ngIfDirective, 66 ngInclude: ngIncludeDirective, 67 ngInit: ngInitDirective, 68 ngNonBindable: ngNonBindableDirective, 69 ngPluralize: ngPluralizeDirective, 70 ngRepeat: ngRepeatDirective, 71 ngShow: ngShowDirective, 72 ngStyle: ngStyleDirective, 73 ngSwitch: ngSwitchDirective, 74 ngSwitchWhen: ngSwitchWhenDirective, 75 ngSwitchDefault: ngSwitchDefaultDirective, 76 ngOptions: ngOptionsDirective, 77 ngTransclude: ngTranscludeDirective, 78 ngModel: ngModelDirective, 79 ngList: ngListDirective, 80 ngChange: ngChangeDirective, 81 pattern: patternDirective, 82 ngPattern: patternDirective, 83 required: requiredDirective, 84 ngRequired: requiredDirective, 85 minlength: minlengthDirective, 86 ngMinlength: minlengthDirective, 87 maxlength: maxlengthDirective, 88 ngMaxlength: maxlengthDirective, 89 ngValue: ngValueDirective, 90 ngModelOptions: ngModelOptionsDirective 91 }). 92 directive({ 93 ngInclude: ngIncludeFillContentDirective 94 }). 95 directive(ngAttributeAliasDirectives). 96 directive(ngEventDirectives); 97 $provide.provider({ 98 $anchorScroll: $AnchorScrollProvider, 99 $animate: $AnimateProvider, 100 $browser: $BrowserProvider, 101 $cacheFactory: $CacheFactoryProvider, 102 $controller: $ControllerProvider, 103 $document: $DocumentProvider, 104 $exceptionHandler: $ExceptionHandlerProvider, 105 $filter: $FilterProvider, 106 $interpolate: $InterpolateProvider, 107 $interval: $IntervalProvider, 108 $http: $HttpProvider, 109 $httpBackend: $HttpBackendProvider, 110 $location: $LocationProvider, 111 $log: $LogProvider, 112 $parse: $ParseProvider, 113 $rootScope: $RootScopeProvider, 114 $q: $QProvider, 115 $$q: $$QProvider, 116 $sce: $SceProvider, 117 $sceDelegate: $SceDelegateProvider, 118 $sniffer: $SnifferProvider, 119 $templateCache: $TemplateCacheProvider, 120 $timeout: $TimeoutProvider, 121 $window: $WindowProvider, 122 $$rAF: $$RAFProvider, 123 $$asyncCallback : $$AsyncCallbackProvider 124 }); 125 } 126 ]); 127 } 128 129 /* global JQLitePrototype: true, 130 addEventListenerFn: true, 131 removeEventListenerFn: true, 132 BOOLEAN_ATTR: true, 133 ALIASED_ATTR: true, 134 */ 135 136 ////////////////////////////////// 137 //JQLite 138 ////////////////////////////////// 139 140 /** 141 * @ngdoc function 142 * @name angular.element 143 * @module ng 144 * @kind function 145 * 146 * @description 147 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. 148 * 149 * If jQuery is available, `angular.element` is an alias for the 150 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` 151 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." 152 * 153 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows 154 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most 155 * commonly needed functionality with the goal of having a very small footprint.</div> 156 * 157 * To use jQuery, simply load it before `DOMContentLoaded` event fired. 158 * 159 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or 160 * jqLite; they are never raw DOM references.</div> 161 * 162 * ## Angular's jqLite 163 * jqLite provides only the following jQuery methods: 164 * 165 * - [`addClass()`](http://api.jquery.com/addClass/) 166 * - [`after()`](http://api.jquery.com/after/) 167 * - [`append()`](http://api.jquery.com/append/) 168 * - [`attr()`](http://api.jquery.com/attr/) 169 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData 170 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors 171 * - [`clone()`](http://api.jquery.com/clone/) 172 * - [`contents()`](http://api.jquery.com/contents/) 173 * - [`css()`](http://api.jquery.com/css/) 174 * - [`data()`](http://api.jquery.com/data/) 175 * - [`empty()`](http://api.jquery.com/empty/) 176 * - [`eq()`](http://api.jquery.com/eq/) 177 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name 178 * - [`hasClass()`](http://api.jquery.com/hasClass/) 179 * - [`html()`](http://api.jquery.com/html/) 180 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors 181 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData 182 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors 183 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors 184 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors 185 * - [`prepend()`](http://api.jquery.com/prepend/) 186 * - [`prop()`](http://api.jquery.com/prop/) 187 * - [`ready()`](http://api.jquery.com/ready/) 188 * - [`remove()`](http://api.jquery.com/remove/) 189 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) 190 * - [`removeClass()`](http://api.jquery.com/removeClass/) 191 * - [`removeData()`](http://api.jquery.com/removeData/) 192 * - [`replaceWith()`](http://api.jquery.com/replaceWith/) 193 * - [`text()`](http://api.jquery.com/text/) 194 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) 195 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. 196 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces 197 * - [`val()`](http://api.jquery.com/val/) 198 * - [`wrap()`](http://api.jquery.com/wrap/) 199 * 200 * ## jQuery/jqLite Extras 201 * Angular also provides the following additional methods and events to both jQuery and jqLite: 202 * 203 * ### Events 204 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event 205 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM 206 * element before it is removed. 207 * 208 * ### Methods 209 * - `controller(name)` - retrieves the controller of the current element or its parent. By default 210 * 返回當前元素的控制器或它的父節點。 211 * retrieves controller associated with the `ngController` directive. If `name` is provided as、 212 * 默認的返回與 之關聯的控制器,若是ame是camelCase命名的指令 213 * camelCase directive name, then the controller for this directive will be retrieved (e.g. 214 * 這個控制器將會被直接返回 215 * `'ngModel'`).例如,ngModel 216 * - `injector()` - retrieves the injector of the current element or its parent. 217 * 返回當前元素的注入器或它的父節點 218 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current 219 * element or its parent. 220 * 返回當前元素的頂級做用域或它的父元素 221 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the 222 * 若是一旦被附加到當前的元素 223 * current element. This getter should be used only on elements that contain a directive which starts a new isolate 224 * 當啓用一個新的隔離做用域時,它只能做用於某一個元素,包括指令 225 * scope. Calling `scope()` on this element always returns the original non-isolate scope. 226 * 當在這個當前元素調用scope()方法時,它將老是返回其原來的非隔離做用域 227 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top 228 * parent element is reached. 229 * 230 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. 231 * @returns {Object} jQuery object. 232 */ 233 234 JQLite.expando = 'ng339'; 235 236 var jqCache = JQLite.cache = {}, 237 jqId = 1, 238 addEventListenerFn = (window.document.addEventListener 239 ? function(element, type, fn) {element.addEventListener(type, fn, false);} 240 : function(element, type, fn) {element.attachEvent('on' + type, fn);}), 241 removeEventListenerFn = (window.document.removeEventListener 242 ? function(element, type, fn) {element.removeEventListener(type, fn, false); } 243 : function(element, type, fn) {element.detachEvent('on' + type, fn); }); 244 245 /* 246 * !!! This is an undocumented "private" function !!! 247 */ 248 var jqData = JQLite._data = function(node) { 249 //jQuery always returns an object on cache miss 250 //jQuery老是從cache返回一個對象 251 return this.cache[node[this.expando]] || {}; 252 }; 253 254 function jqNextId() { return ++jqId; } 255 256 257 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; 258 var MOZ_HACK_REGEXP = /^moz([A-Z])/; 259 var jqLiteMinErr = minErr('jqLite'); 260 261 /** 262 * Converts snake_case to camelCase. 263 * Also there is special case for Moz prefix starting with upper case letter. 264 * @param name Name to normalize 265 */ 266 function camelCase(name) { 267 return name. 268 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { 269 return offset ? letter.toUpperCase() : letter; 270 }). 271 replace(MOZ_HACK_REGEXP, 'Moz$1'); 272 } 273 274 var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; 275 var HTML_REGEXP = /<|&#?\w+;/; 276 var TAG_NAME_REGEXP = /<([\w:]+)/; 277 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; 278 279 var wrapMap = { 280 'option': [1, '<select multiple="multiple">', '</select>'], 281 282 'thead': [1, '<table>', '</table>'], 283 'col': [2, '<table><colgroup>', '</colgroup></table>'], 284 'tr': [2, '<table><tbody>', '</tbody></table>'], 285 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], 286 '_default': [0, "", ""] 287 }; 288 289 wrapMap.optgroup = wrapMap.option; 290 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 291 wrapMap.th = wrapMap.td; 292 293 function jqLiteIsTextNode(html) { 294 return !HTML_REGEXP.test(html); 295 } 296 297 function jqLiteAcceptsData(node) { 298 // The window object can accept data but has no nodeType 299 // Otherwise we are only interested in elements (1) and documents (9) 300 return !node.nodeType || node.nodeType === 1 || node.nodeType === 9; 301 } 302 303 function jqLiteBuildFragment(html, context) { 304 var elem, tmp, tag, wrap, 305 fragment = context.createDocumentFragment(), 306 nodes = [], i; 307 308 if (jqLiteIsTextNode(html)) { 309 // Convert non-html into a text node 310 nodes.push(context.createTextNode(html)); 311 } else { 312 // Convert html into DOM nodes 313 tmp = tmp || fragment.appendChild(context.createElement("div")); 314 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); 315 wrap = wrapMap[tag] || wrapMap._default; 316 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2]; 317 318 // Descend through wrappers to the right content 319 i = wrap[0]; 320 while (i--) { 321 tmp = tmp.lastChild; 322 } 323 324 nodes = concat(nodes, tmp.childNodes); 325 326 tmp = fragment.firstChild; 327 tmp.textContent = ""; 328 } 329 330 // Remove wrapper from fragment 331 fragment.textContent = ""; 332 fragment.innerHTML = ""; // Clear inner HTML 333 forEach(nodes, function(node) { 334 fragment.appendChild(node); 335 }); 336 337 return fragment; 338 } 339 340 function jqLiteParseHTML(html, context) { 341 context = context || document; 342 var parsed; 343 344 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { 345 return [context.createElement(parsed[1])]; 346 } 347 348 if ((parsed = jqLiteBuildFragment(html, context))) { 349 return parsed.childNodes; 350 } 351 352 return []; 353 } 354 355 ///////////////////////////////////////////// 356 function JQLite(element) { 357 if (element instanceof JQLite) { 358 return element; 359 } 360 if (isString(element)) { 361 element = trim(element); 362 } 363 if (!(this instanceof JQLite)) { 364 if (isString(element) && element.charAt(0) != '<') { 365 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); 366 } 367 return new JQLite(element); 368 } 369 370 if (isString(element)) { 371 jqLiteAddNodes(this, jqLiteParseHTML(element)); 372 } else { 373 jqLiteAddNodes(this, element); 374 } 375 } 376 377 function jqLiteClone(element) { 378 return element.cloneNode(true); 379 } 380 381 function jqLiteDealoc(element, onlyDescendants){ 382 if (!onlyDescendants) jqLiteRemoveData(element); 383 384 if (element.childNodes && element.childNodes.length) { 385 // we use querySelectorAll because documentFragments don't have getElementsByTagName 386 var descendants = element.getElementsByTagName ? element.getElementsByTagName('*') : 387 element.querySelectorAll ? element.querySelectorAll('*') : []; 388 for (var i = 0, l = descendants.length; i < l; i++) { 389 jqLiteRemoveData(descendants[i]); 390 } 391 } 392 } 393 394 function jqLiteOff(element, type, fn, unsupported) { 395 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); 396 397 var events = jqLiteExpandoStore(element, 'events'), 398 handle = jqLiteExpandoStore(element, 'handle'); 399 400 if (!handle) return; //no listeners registered 401 402 if (isUndefined(type)) { 403 forEach(events, function(eventHandler, type) { 404 removeEventListenerFn(element, type, eventHandler); 405 delete events[type]; 406 }); 407 } else { 408 forEach(type.split(' '), function(type) { 409 if (isUndefined(fn)) { 410 removeEventListenerFn(element, type, events[type]); 411 delete events[type]; 412 } else { 413 arrayRemove(events[type] || [], fn); 414 } 415 }); 416 } 417 } 418 419 function jqLiteRemoveData(element, name) { 420 var expandoId = element.ng339, 421 expandoStore = jqCache[expandoId]; 422 423 if (expandoStore) { 424 if (name) { 425 delete jqCache[expandoId].data[name]; 426 return; 427 } 428 429 if (expandoStore.handle) { 430 expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); 431 jqLiteOff(element); 432 } 433 delete jqCache[expandoId]; 434 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it 435 } 436 } 437 438 function jqLiteExpandoStore(element, key, value) { 439 var expandoId = element.ng339, 440 expandoStore = jqCache[expandoId || -1]; 441 442 if (isDefined(value)) { 443 if (!expandoStore) { 444 element.ng339 = expandoId = jqNextId(); 445 expandoStore = jqCache[expandoId] = {}; 446 } 447 expandoStore[key] = value; 448 } else { 449 return expandoStore && expandoStore[key]; 450 } 451 } 452 453 function jqLiteData(element, key, value) { 454 if (jqLiteAcceptsData(element)) { 455 var data = jqLiteExpandoStore(element, 'data'), 456 isSetter = isDefined(value), 457 keyDefined = !isSetter && isDefined(key), 458 isSimpleGetter = keyDefined && !isObject(key); 459 460 if (!data && !isSimpleGetter) { 461 jqLiteExpandoStore(element, 'data', data = {}); 462 } 463 464 if (isSetter) { 465 data[key] = value; 466 } else { 467 if (keyDefined) { 468 if (isSimpleGetter) { 469 // don't create data in this case. 470 return data && data[key]; 471 } else { 472 extend(data, key); 473 } 474 } else { 475 return data; 476 } 477 } 478 } 479 } 480 481 function jqLiteHasClass(element, selector) { 482 if (!element.getAttribute) return false; 483 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). 484 indexOf( " " + selector + " " ) > -1); 485 } 486 487 function jqLiteRemoveClass(element, cssClasses) { 488 if (cssClasses && element.setAttribute) { 489 forEach(cssClasses.split(' '), function(cssClass) { 490 element.setAttribute('class', trim( 491 (" " + (element.getAttribute('class') || '') + " ") 492 .replace(/[\n\t]/g, " ") 493 .replace(" " + trim(cssClass) + " ", " ")) 494 ); 495 }); 496 } 497 } 498 499 function jqLiteAddClass(element, cssClasses) { 500 if (cssClasses && element.setAttribute) { 501 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') 502 .replace(/[\n\t]/g, " "); 503 504 forEach(cssClasses.split(' '), function(cssClass) { 505 cssClass = trim(cssClass); 506 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { 507 existingClasses += cssClass + ' '; 508 } 509 }); 510 511 element.setAttribute('class', trim(existingClasses)); 512 } 513 } 514 515 516 function jqLiteAddNodes(root, elements) { 517 // THIS CODE IS VERY HOT. Don't make changes without benchmarking. 518 519 if (elements) { 520 521 // if a Node (the most common case) 522 if (elements.nodeType) { 523 root[root.length++] = elements; 524 } else { 525 var length = elements.length; 526 527 // if an Array or NodeList and not a Window 528 if (typeof length === 'number' && elements.window !== elements) { 529 if (length) { 530 if (elements.item) { 531 // convert NodeList to an Array to make PhantomJS 1.x happy 532 elements = slice.call(elements); 533 } 534 push.apply(root, elements); 535 } 536 } else { 537 root[root.length++] = elements; 538 } 539 } 540 } 541 } 542 543 544 function jqLiteController(element, name) { 545 return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); 546 } 547 548 function jqLiteInheritedData(element, name, value) { 549 element = jqLite(element); 550 551 // if element is the document object work with the html element instead 552 // this makes $(document).scope() possible 553 if(element[0].nodeType == 9) { 554 element = element.find('html'); 555 } 556 var names = isArray(name) ? name : [name]; 557 558 while (element.length) { 559 var node = element[0]; 560 for (var i = 0, ii = names.length; i < ii; i++) { 561 if ((value = element.data(names[i])) !== undefined) return value; 562 } 563 564 // If dealing with a document fragment node with a host element, and no parent, use the host 565 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM 566 // to lookup parent controllers. 567 element = jqLite(node.parentNode || (node.nodeType === 11 && node.host)); 568 } 569 } 570 571 function jqLiteEmpty(element) { 572 jqLiteDealoc(element, true); 573 while (element.firstChild) { 574 element.removeChild(element.firstChild); 575 } 576 } 577 578 ////////////////////////////////////////// 579 // Functions which are declared directly. 580 ////////////////////////////////////////// 581 var JQLitePrototype = JQLite.prototype = { 582 ready: function(fn) { 583 var fired = false; 584 585 function trigger() { 586 if (fired) return; 587 fired = true; 588 fn(); 589 } 590 591 // check if document already is loaded 592 if (document.readyState === 'complete'){//判斷文檔是否已經加載完成 593 setTimeout(trigger); 594 } else { 595 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 596 // we can not use jqLite since we are not done loading and jQuery could be loaded later. 597 // jshint -W064 598 JQLite(window).on('load', trigger); // fallback to window.onload for others 599 // jshint +W064 600 } 601 }, 602 toString: function() { 603 var value = []; 604 forEach(this, function(e){ value.push('' + e);}); 605 return '[' + value.join(', ') + ']'; 606 }, 607 608 eq: function(index) { 609 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); 610 }, 611 612 length: 0, 613 push: push, 614 sort: [].sort, 615 splice: [].splice 616 }; 617 618 ////////////////////////////////////////// 619 // Functions iterating getter/setters. 620 // these functions return self on setter and 621 // value on get. 622 ////////////////////////////////////////// 623 var BOOLEAN_ATTR = {}; 624 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { 625 BOOLEAN_ATTR[lowercase(value)] = value; 626 }); 627 var BOOLEAN_ELEMENTS = {}; 628 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { 629 BOOLEAN_ELEMENTS[value] = true; 630 }); 631 var ALIASED_ATTR = { 632 'ngMinlength' : 'minlength', 633 'ngMaxlength' : 'maxlength', 634 'ngPattern' : 'pattern' 635 }; 636 637 function getBooleanAttrName(element, name) { 638 // check dom last since we will most likely fail on name 639 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; 640 641 // booleanAttr is here twice to minimize DOM access 642 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; 643 } 644 645 function getAliasedAttrName(element, name) { 646 var nodeName = element.nodeName; 647 return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name]; 648 } 649 650 forEach({ 651 data: jqLiteData, 652 inheritedData: jqLiteInheritedData, 653 654 scope: function(element) { 655 // Can't use jqLiteData here directly so we stay compatible with jQuery! 656 return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); 657 }, 658 659 isolateScope: function(element) { 660 // Can't use jqLiteData here directly so we stay compatible with jQuery! 661 return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate'); 662 }, 663 664 controller: jqLiteController, 665 666 injector: function(element) { 667 return jqLiteInheritedData(element, '$injector'); 668 }, 669 670 removeAttr: function(element,name) { 671 element.removeAttribute(name); 672 }, 673 674 hasClass: jqLiteHasClass, 675 676 css: function(element, name, value) { 677 name = camelCase(name); 678 679 if (isDefined(value)) { 680 element.style[name] = value; 681 } else { 682 var val; 683 684 if (msie <= 8) { 685 // this is some IE specific weirdness that jQuery 1.6.4 does not sure why 686 val = element.currentStyle && element.currentStyle[name]; 687 if (val === '') val = 'auto'; 688 } 689 690 val = val || element.style[name]; 691 692 if (msie <= 8) { 693 // jquery weirdness :-/ 694 val = (val === '') ? undefined : val; 695 } 696 697 return val; 698 } 699 }, 700 701 attr: function(element, name, value){ 702 var lowercasedName = lowercase(name); 703 if (BOOLEAN_ATTR[lowercasedName]) { 704 if (isDefined(value)) { 705 if (!!value) { 706 element[name] = true; 707 element.setAttribute(name, lowercasedName); 708 } else { 709 element[name] = false; 710 element.removeAttribute(lowercasedName); 711 } 712 } else { 713 return (element[name] || 714 (element.attributes.getNamedItem(name)|| noop).specified) 715 ? lowercasedName 716 : undefined; 717 } 718 } else if (isDefined(value)) { 719 element.setAttribute(name, value); 720 } else if (element.getAttribute) { 721 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code 722 // some elements (e.g. Document) don't have get attribute, so return undefined 723 var ret = element.getAttribute(name, 2); 724 // normalize non-existing attributes to undefined (as jQuery) 725 return ret === null ? undefined : ret; 726 } 727 }, 728 729 prop: function(element, name, value) { 730 if (isDefined(value)) { 731 element[name] = value; 732 } else { 733 return element[name]; 734 } 735 }, 736 737 text: (function() { 738 getText.$dv = ''; 739 return getText; 740 741 function getText(element, value) { 742 if (isUndefined(value)) { 743 var nodeType = element.nodeType; 744 return (nodeType === 1 || nodeType === 3) ? element.textContent : ''; 745 } 746 element.textContent = value; 747 } 748 })(), 749 750 val: function(element, value) { 751 if (isUndefined(value)) { 752 if (element.multiple && nodeName_(element) === 'select') { 753 var result = []; 754 forEach(element.options, function (option) { 755 if (option.selected) { 756 result.push(option.value || option.text); 757 } 758 }); 759 return result.length === 0 ? null : result; 760 } 761 return element.value; 762 } 763 element.value = value; 764 }, 765 766 html: function(element, value) { 767 if (isUndefined(value)) { 768 return element.innerHTML; 769 } 770 jqLiteDealoc(element, true); 771 element.innerHTML = value; 772 }, 773 774 empty: jqLiteEmpty 775 }, function(fn, name){ 776 /** 777 * Properties: writes return selection, reads return first value 778 */ 779 JQLite.prototype[name] = function(arg1, arg2) { 780 var i, key; 781 var nodeCount = this.length; 782 783 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it 784 // in a way that survives minification. 785 // jqLiteEmpty takes no arguments but is a setter. 786 if (fn !== jqLiteEmpty && 787 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { 788 if (isObject(arg1)) { 789 790 // we are a write, but the object properties are the key/values 791 for (i = 0; i < nodeCount; i++) { 792 if (fn === jqLiteData) { 793 // data() takes the whole object in jQuery 794 fn(this[i], arg1); 795 } else { 796 for (key in arg1) { 797 fn(this[i], key, arg1[key]); 798 } 799 } 800 } 801 // return self for chaining 802 return this; 803 } else { 804 // we are a read, so read the first child. 805 // TODO: do we still need this? 806 var value = fn.$dv; 807 // Only if we have $dv do we iterate over all, otherwise it is just the first element. 808 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; 809 for (var j = 0; j < jj; j++) { 810 var nodeValue = fn(this[j], arg1, arg2); 811 value = value ? value + nodeValue : nodeValue; 812 } 813 return value; 814 } 815 } else { 816 // we are a write, so apply to all children 817 for (i = 0; i < nodeCount; i++) { 818 fn(this[i], arg1, arg2); 819 } 820 // return self for chaining 821 return this; 822 } 823 }; 824 }); 825 826 function createEventHandler(element, events) { 827 var eventHandler = function (event, type) { 828 if (!event.preventDefault) { 829 event.preventDefault = function() { 830 event.returnValue = false; //ie 831 }; 832 } 833 834 if (!event.stopPropagation) { 835 event.stopPropagation = function() { 836 event.cancelBubble = true; //ie 837 }; 838 } 839 840 if (!event.target) { 841 event.target = event.srcElement || document; 842 } 843 844 if (isUndefined(event.defaultPrevented)) { 845 var prevent = event.preventDefault; 846 event.preventDefault = function() { 847 event.defaultPrevented = true; 848 prevent.call(event); 849 }; 850 event.defaultPrevented = false; 851 } 852 853 event.isDefaultPrevented = function() { 854 return event.defaultPrevented || event.returnValue === false; 855 }; 856 857 // Copy event handlers in case event handlers array is modified during execution. 858 var eventHandlersCopy = shallowCopy(events[type || event.type] || []); 859 860 forEach(eventHandlersCopy, function(fn) { 861 fn.call(element, event); 862 }); 863 864 // Remove monkey-patched methods (IE), 865 // as they would cause memory leaks in IE8. 866 if (msie <= 8) { 867 // IE7/8 does not allow to delete property on native object 868 event.preventDefault = null; 869 event.stopPropagation = null; 870 event.isDefaultPrevented = null; 871 } else { 872 // It shouldn't affect normal browsers (native methods are defined on prototype). 873 delete event.preventDefault; 874 delete event.stopPropagation; 875 delete event.isDefaultPrevented; 876 } 877 }; 878 eventHandler.elem = element; 879 return eventHandler; 880 } 881 882 ////////////////////////////////////////// 883 // Functions iterating traversal. 884 // These functions chain results into a single 885 // selector. 886 ////////////////////////////////////////// 887 forEach({ 888 removeData: jqLiteRemoveData, 889 890 on: function onFn(element, type, fn, unsupported){ 891 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); 892 893 // Do not add event handlers to non-elements because they will not be cleaned up. 894 if (!jqLiteAcceptsData(element)) { 895 return; 896 } 897 898 var events = jqLiteExpandoStore(element, 'events'), 899 handle = jqLiteExpandoStore(element, 'handle'); 900 901 if (!events) jqLiteExpandoStore(element, 'events', events = {}); 902 if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); 903 904 forEach(type.split(' '), function(type){ 905 var eventFns = events[type]; 906 907 if (!eventFns) { 908 if (type == 'mouseenter' || type == 'mouseleave') { 909 var contains = document.body.contains || document.body.compareDocumentPosition ? 910 function( a, b ) { 911 // jshint bitwise: false 912 var adown = a.nodeType === 9 ? a.documentElement : a, 913 bup = b && b.parentNode; 914 return a === bup || !!( bup && bup.nodeType === 1 && ( 915 adown.contains ? 916 adown.contains( bup ) : 917 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 918 )); 919 } : 920 function( a, b ) { 921 if ( b ) { 922 while ( (b = b.parentNode) ) { 923 if ( b === a ) { 924 return true; 925 } 926 } 927 } 928 return false; 929 }; 930 931 events[type] = []; 932 933 // Refer to jQuery's implementation of mouseenter & mouseleave 934 // Read about mouseenter and mouseleave: 935 // http://www.quirksmode.org/js/events_mouse.html#link8 936 var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}; 937 938 onFn(element, eventmap[type], function(event) { 939 var target = this, related = event.relatedTarget; 940 // For mousenter/leave call the handler if related is outside the target. 941 // NB: No relatedTarget if the mouse left/entered the browser window 942 if ( !related || (related !== target && !contains(target, related)) ){ 943 handle(event, type); 944 } 945 }); 946 947 } else { 948 addEventListenerFn(element, type, handle); 949 events[type] = []; 950 } 951 eventFns = events[type]; 952 } 953 eventFns.push(fn); 954 }); 955 }, 956 957 off: jqLiteOff, 958 959 one: function(element, type, fn) { 960 element = jqLite(element); 961 962 //add the listener twice so that when it is called 963 //you can remove the original function and still be 964 //able to call element.off(ev, fn) normally 965 element.on(type, function onFn() { 966 element.off(type, fn); 967 element.off(type, onFn); 968 }); 969 element.on(type, fn); 970 }, 971 972 replaceWith: function(element, replaceNode) { 973 var index, parent = element.parentNode; 974 jqLiteDealoc(element); 975 forEach(new JQLite(replaceNode), function(node){ 976 if (index) { 977 parent.insertBefore(node, index.nextSibling); 978 } else { 979 parent.replaceChild(node, element); 980 } 981 index = node; 982 }); 983 }, 984 985 children: function(element) { 986 var children = []; 987 forEach(element.childNodes, function(element){ 988 if (element.nodeType === 1) 989 children.push(element); 990 }); 991 return children; 992 }, 993 994 contents: function(element) { 995 return element.contentDocument || element.childNodes || []; 996 }, 997 998 append: function(element, node) { 999 forEach(new JQLite(node), function(child){ 1000 if (element.nodeType === 1 || element.nodeType === 11) { 1001 element.appendChild(child); 1002 } 1003 }); 1004 }, 1005 1006 prepend: function(element, node) { 1007 if (element.nodeType === 1) { 1008 var index = element.firstChild; 1009 forEach(new JQLite(node), function(child){ 1010 element.insertBefore(child, index); 1011 }); 1012 } 1013 }, 1014 1015 wrap: function(element, wrapNode) { 1016 wrapNode = jqLite(wrapNode)[0]; 1017 var parent = element.parentNode; 1018 if (parent) { 1019 parent.replaceChild(wrapNode, element); 1020 } 1021 wrapNode.appendChild(element); 1022 }, 1023 1024 remove: function(element) { 1025 jqLiteDealoc(element); 1026 var parent = element.parentNode; 1027 if (parent) parent.removeChild(element); 1028 }, 1029 1030 after: function(element, newElement) { 1031 var index = element, parent = element.parentNode; 1032 forEach(new JQLite(newElement), function(node){ 1033 parent.insertBefore(node, index.nextSibling); 1034 index = node; 1035 }); 1036 }, 1037 1038 addClass: jqLiteAddClass, 1039 removeClass: jqLiteRemoveClass, 1040 1041 toggleClass: function(element, selector, condition) { 1042 if (selector) { 1043 forEach(selector.split(' '), function(className){ 1044 var classCondition = condition; 1045 if (isUndefined(classCondition)) { 1046 classCondition = !jqLiteHasClass(element, className); 1047 } 1048 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); 1049 }); 1050 } 1051 }, 1052 1053 parent: function(element) { 1054 var parent = element.parentNode; 1055 return parent && parent.nodeType !== 11 ? parent : null; 1056 }, 1057 1058 next: function(element) { 1059 if (element.nextElementSibling) { 1060 return element.nextElementSibling; 1061 } 1062 1063 // IE8 doesn't have nextElementSibling 1064 var elm = element.nextSibling; 1065 while (elm != null && elm.nodeType !== 1) { 1066 elm = elm.nextSibling; 1067 } 1068 return elm; 1069 }, 1070 1071 find: function(element, selector) { 1072 if (element.getElementsByTagName) { 1073 return element.getElementsByTagName(selector); 1074 } else { 1075 return []; 1076 } 1077 }, 1078 1079 clone: jqLiteClone, 1080 1081 triggerHandler: function(element, eventName, eventData) { 1082 var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName]; 1083 1084 eventData = eventData || []; 1085 1086 var event = [{ 1087 preventDefault: function() { 1088 this.defaultPrevented = true; 1089 }, 1090 isDefaultPrevented: function() { 1091 return this.defaultPrevented === true; 1092 }, 1093 stopPropagation: noop 1094 }]; 1095 1096 forEach(eventFns, function(fn) { 1097 fn.apply(element, event.concat(eventData)); 1098 }); 1099 } 1100 }, function(fn, name){ 1101 /** 1102 * chaining functions 1103 */ 1104 JQLite.prototype[name] = function(arg1, arg2, arg3) { 1105 var value; 1106 for(var i=0; i < this.length; i++) { 1107 if (isUndefined(value)) { 1108 value = fn(this[i], arg1, arg2, arg3); 1109 if (isDefined(value)) { 1110 // any function which returns a value needs to be wrapped 1111 value = jqLite(value); 1112 } 1113 } else { 1114 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); 1115 } 1116 } 1117 return isDefined(value) ? value : this; 1118 }; 1119 1120 // bind legacy bind/unbind to on/off 1121 JQLite.prototype.bind = JQLite.prototype.on; 1122 JQLite.prototype.unbind = JQLite.prototype.off; 1123 }); 1124 1125 /** 1126 * Computes a hash of an 'obj'. 1127 * Hash of a: 1128 * string is string 1129 * number is number as string 1130 * object is either result of calling $$hashKey function on the object or uniquely generated id, 1131 * that is also assigned to the $$hashKey property of the object. 1132 * 1133 * @param obj 1134 * @returns {string} hash string such that the same input will have the same hash string. 1135 * The resulting string key is in 'type:hashKey' format. 1136 */ 1137 function hashKey(obj, nextUidFn) { 1138 var objType = typeof obj, 1139 key; 1140 1141 if (objType == 'function' || (objType == 'object' && obj !== null)) { 1142 if (typeof (key = obj.$$hashKey) == 'function') { 1143 // must invoke on object to keep the right this 1144 key = obj.$$hashKey(); 1145 } else if (key === undefined) { 1146 key = obj.$$hashKey = (nextUidFn || nextUid)(); 1147 } 1148 } else { 1149 key = obj; 1150 } 1151 1152 return objType + ':' + key; 1153 } 1154 1155 /** 1156 * HashMap which can use objects as keys 1157 */ 1158 function HashMap(array, isolatedUid) { 1159 if (isolatedUid) { 1160 var uid = 0; 1161 this.nextUid = function() { 1162 return ++uid; 1163 }; 1164 } 1165 forEach(array, this.put, this); 1166 } 1167 HashMap.prototype = { 1168 /** 1169 * Store key value pair 1170 * @param key key to store can be any type 1171 * @param value value to store can be any type 1172 */ 1173 put: function(key, value) { 1174 this[hashKey(key, this.nextUid)] = value; 1175 }, 1176 1177 /** 1178 * @param key 1179 * @returns {Object} the value for the key 1180 */ 1181 get: function(key) { 1182 return this[hashKey(key, this.nextUid)]; 1183 }, 1184 1185 /** 1186 * Remove the key/value pair 1187 * @param key 1188 */ 1189 remove: function(key) { 1190 var value = this[key = hashKey(key, this.nextUid)]; 1191 delete this[key]; 1192 return value; 1193 } 1194 }; 1195 1196 /** 1197 * @ngdoc function 1198 * @module ng 1199 * @name angular.injector 1200 * @kind function 1201 * 1202 * @description 1203 * Creates an injector function that can be used for retrieving services as well as for 1204 * dependency injection (see {@link guide/di dependency injection}). 1205 * 1206 1207 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See 1208 * {@link angular.module}. The `ng` module must be explicitly added. 1209 * @returns {function()} Injector function. See {@link auto.$injector $injector}. 1210 * 1211 * @example 1212 * Typical usage 1213 * ```js 1214 * // create an injector 1215 * var $injector = angular.injector(['ng']); 1216 * 1217 * // use the injector to kick off your application 1218 * // use the type inference to auto inject arguments, or use implicit injection 1219 * $injector.invoke(function($rootScope, $compile, $document){ 1220 * $compile($document)($rootScope); 1221 * $rootScope.$digest(); 1222 * }); 1223 * ``` 1224 * 1225 * Sometimes you want to get access to the injector of a currently running Angular app 1226 * from outside Angular. Perhaps, you want to inject and compile some markup after the 1227 * application has been bootstrapped. You can do this using the extra `injector()` added 1228 * to JQuery/jqLite elements. See {@link angular.element}. 1229 * 1230 * *This is fairly rare but could be the case if a third party library is injecting the 1231 * markup.* 1232 * 1233 * In the following example a new block of HTML containing a `ng-controller` 1234 * directive is added to the end of the document body by JQuery. We then compile and link 1235 * it into the current AngularJS scope. 1236 * 1237 * ```js 1238 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); 1239 * $(document.body).append($div); 1240 * 1241 * angular.element(document).injector().invoke(function($compile) { 1242 * var scope = angular.element($div).scope(); 1243 * $compile($div)(scope); 1244 * }); 1245 * ``` 1246 */ 1247 1248 1249 /** 1250 * @ngdoc module 1251 * @name auto 1252 * @description 1253 * 1254 * Implicit module which gets automatically added to each {@link auto.$injector $injector}. 1255 */ 1256 1257 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; 1258 var FN_ARG_SPLIT = /,/; 1259 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; 1260 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 1261 var $injectorMinErr = minErr('$injector'); 1262 1263 function anonFn(fn) { 1264 // For anonymous functions, showing at the very least the function signature can help in 1265 // debugging. 1266 var fnText = fn.toString().replace(STRIP_COMMENTS, ''), 1267 args = fnText.match(FN_ARGS); 1268 if (args) { 1269 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; 1270 } 1271 return 'fn'; 1272 } 1273 1274 function annotate(fn, strictDi, name) { 1275 var $inject, 1276 fnText, 1277 argDecl, 1278 last; 1279 1280 if (typeof fn === 'function') { 1281 if (!($inject = fn.$inject)) { 1282 $inject = []; 1283 if (fn.length) { 1284 if (strictDi) { 1285 if (!isString(name) || !name) { 1286 name = fn.name || anonFn(fn); 1287 } 1288 throw $injectorMinErr('strictdi', 1289 '{0} is not using explicit annotation and cannot be invoked in strict mode', name); 1290 } 1291 fnText = fn.toString().replace(STRIP_COMMENTS, ''); 1292 argDecl = fnText.match(FN_ARGS); 1293 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ 1294 arg.replace(FN_ARG, function(all, underscore, name){ 1295 $inject.push(name); 1296 }); 1297 }); 1298 } 1299 fn.$inject = $inject; 1300 } 1301 } else if (isArray(fn)) { 1302 last = fn.length - 1; 1303 assertArgFn(fn[last], 'fn'); 1304 $inject = fn.slice(0, last); 1305 } else { 1306 assertArgFn(fn, 'fn', true); 1307 } 1308 return $inject; 1309 } 1310 1311 /////////////////////////////////////// 1312 1313 /** 1314 * @ngdoc service 1315 * @name $injector 1316 * @kind function 1317 * 1318 * @description 1319 * 1320 * `$injector` is used to retrieve object instances as defined by 1321 * {@link auto.$provide provider}, instantiate types, invoke methods, 1322 * and load modules. 1323 * 1324 * The following always holds true: 1325 * 1326 * ```js 1327 * var $injector = angular.injector(); 1328 * expect($injector.get('$injector')).toBe($injector); 1329 * expect($injector.invoke(function($injector){ 1330 * return $injector; 1331 * }).toBe($injector); 1332 * ``` 1333 * 1334 * # Injection Function Annotation 1335 * 1336 * JavaScript does not have annotations, and annotations are needed for dependency injection. The 1337 * following are all valid ways of annotating function with injection arguments and are equivalent. 1338 * 1339 * ```js 1340 * // inferred (only works if code not minified/obfuscated) 1341 * $injector.invoke(function(serviceA){}); 1342 * 1343 * // annotated 1344 * function explicit(serviceA) {}; 1345 * explicit.$inject = ['serviceA']; 1346 * $injector.invoke(explicit); 1347 * 1348 * // inline 1349 * $injector.invoke(['serviceA', function(serviceA){}]); 1350 * ``` 1351 * 1352 * ## Inference 1353 * 1354 * In JavaScript calling `toString()` on a function returns the function definition. The definition 1355 * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with 1356 * minification, and obfuscation tools since these tools change the argument names. 1357 * 1358 * ## `$inject` Annotation 1359 * By adding an `$inject` property onto a function the injection parameters can be specified. 1360 * 1361 * ## Inline 1362 * As an array of injection names, where the last item in the array is the function to call. 1363 */ 1364 1365 /** 1366 * @ngdoc method 1367 * @name $injector#get 1368 * 1369 * @description 1370 * Return an instance of the service. 1371 * 1372 * @param {string} name The name of the instance to retrieve. 1373 * @return {*} The instance. 1374 */ 1375 1376 /** 1377 * @ngdoc method 1378 * @name $injector#invoke 1379 * 1380 * @description 1381 * Invoke the method and supply the method arguments from the `$injector`. 1382 * 1383 * @param {!Function} fn The function to invoke. Function parameters are injected according to the 1384 * {@link guide/di $inject Annotation} rules. 1385 * @param {Object=} self The `this` for the invoked method. 1386 * @param {Object=} locals Optional object. If preset then any argument names are read from this 1387 * object first, before the `$injector` is consulted. 1388 * @returns {*} the value returned by the invoked `fn` function. 1389 */ 1390 1391 /** 1392 * @ngdoc method 1393 * @name $injector#has 1394 * 1395 * @description 1396 * Allows the user to query if the particular service exists. 1397 * 1398 * @param {string} Name of the service to query. 1399 * @returns {boolean} returns true if injector has given service. 1400 */ 1401 1402 /** 1403 * @ngdoc method 1404 * @name $injector#instantiate 1405 * @description 1406 * Create a new instance of JS type. The method takes a constructor function, invokes the new 1407 * operator, and supplies all of the arguments to the constructor function as specified by the 1408 * constructor annotation. 1409 * 1410 * @param {Function} Type Annotated constructor function. 1411 * @param {Object=} locals Optional object. If preset then any argument names are read from this 1412 * object first, before the `$injector` is consulted. 1413 * @returns {Object} new instance of `Type`. 1414 */ 1415 1416 /** 1417 * @ngdoc method 1418 * @name $injector#annotate 1419 * 1420 * @description 1421 * Returns an array of service names which the function is requesting for injection. This API is 1422 * used by the injector to determine which services need to be injected into the function when the 1423 * function is invoked. There are three ways in which the function can be annotated with the needed 1424 * dependencies. 1425 * 1426 * # Argument names 1427 * 1428 * The simplest form is to extract the dependencies from the arguments of the function. This is done 1429 * by converting the function into a string using `toString()` method and extracting the argument 1430 * names. 1431 * ```js 1432 * // Given 1433 * function MyController($scope, $route) { 1434 * // ... 1435 * } 1436 * 1437 * // Then 1438 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 1439 * ``` 1440 * 1441 * This method does not work with code minification / obfuscation. For this reason the following 1442 * annotation strategies are supported. 1443 * 1444 * # The `$inject` property 1445 * 1446 * If a function has an `$inject` property and its value is an array of strings, then the strings 1447 * represent names of services to be injected into the function. 1448 * ```js 1449 * // Given 1450 * var MyController = function(obfuscatedScope, obfuscatedRoute) { 1451 * // ... 1452 * } 1453 * // Define function dependencies 1454 * MyController['$inject'] = ['$scope', '$route']; 1455 * 1456 * // Then 1457 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 1458 * ``` 1459 * 1460 * # The array notation 1461 * 1462 * It is often desirable to inline Injected functions and that's when setting the `$inject` property 1463 * is very inconvenient. In these situations using the array notation to specify the dependencies in 1464 * a way that survives minification is a better choice: 1465 * 1466 * ```js 1467 * // We wish to write this (not minification / obfuscation safe) 1468 * injector.invoke(function($compile, $rootScope) { 1469 * // ... 1470 * }); 1471 * 1472 * // We are forced to write break inlining 1473 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { 1474 * // ... 1475 * }; 1476 * tmpFn.$inject = ['$compile', '$rootScope']; 1477 * injector.invoke(tmpFn); 1478 * 1479 * // To better support inline function the inline annotation is supported 1480 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { 1481 * // ... 1482 * }]); 1483 * 1484 * // Therefore 1485 * expect(injector.annotate( 1486 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) 1487 * ).toEqual(['$compile', '$rootScope']); 1488 * ``` 1489 * 1490 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to 1491 * be retrieved as described above. 1492 * 1493 * @returns {Array.<string>} The names of the services which the function requires. 1494 */ 1495 1496 1497 1498 1499 /** 1500 * @ngdoc service 1501 * @name $provide 1502 * 1503 * @description 1504 * 1505 * The {@link auto.$provide $provide} service has a number of methods for registering components 1506 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on 1507 * {@link angular.Module}. 1508 * 1509 * An Angular **service** is a singleton object created by a **service factory**. These **service 1510 * Angular服務是一個單例的對象經過factory建立 1511 * factories** are functions which, in turn, are created by a **service provider**. 1512 * 這個factory是一個函數,反過來講則經過服務的提供者建立 1513 * The **service providers** are constructor functions. When instantiated they must contain a 1514 * 服務提供者是一個構造函數,當須要被實例化的時候,它們則必須包含一個$get的屬性 1515 * property called `$get`, which holds the **service factory** function. 1516 *包含了服務工廠函數 1517 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the 1518 * correct **service provider**, instantiating it and then calling its `$get` **service factory** 1519 * function to get the instance of the **service**. 1520 *當你請求一個服務時,注入器會去查找服務提供者,實例化而且調用它的$get服務工廠獲取上面請求的實例。 1521 * Often services have no configuration options and there is no need to add methods to the service 1522 * 其餘的服務沒有配置項而且不須要給服務提供者添加多餘的方法 1523 * provider. The provider will be no more than a constructor function with a `$get` property. For 1524 * 提供者不只僅是一個包含$get屬性的構造函數,它還包括其餘的內容 1525 * these cases the {@link auto.$provide $provide} service has additional helper methods to register 1526 * services without specifying a provider. 1527 * 1528 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the 1529 * {@link auto.$injector $injector}注入器 1530 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by 1531 * providers and services.常量,注入一個鍵值對,val/objec 1532 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by 1533 * services, not providers. 1534 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, 1535 * that will be wrapped in a **service provider** object, whose `$get` property will contain the 1536 * given factory function. 1537 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` 1538 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate 1539 * a new object using the given constructor function. 1540 * 1541 * See the individual methods for more information and examples. 1542 */ 1543 1544 /** 1545 * @ngdoc method 1546 * @name $provide#provider 1547 * @description 1548 * 1549 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions 1550 * are constructor functions, whose instances are responsible for "providing" a factory for a 1551 * service. 1552 * 1553 * Service provider names start with the name of the service they provide followed by `Provider`. 1554 * For example, the {@link ng.$log $log} service has a provider called 1555 * {@link ng.$logProvider $logProvider}. 1556 * 1557 * Service provider objects can have additional methods which allow configuration of the provider 1558 * and its service. Importantly, you can configure what kind of service is created by the `$get` 1559 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a 1560 * method {@link ng.$logProvider#debugEnabled debugEnabled} 1561 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the 1562 * console or not. 1563 * 1564 * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 1565 'Provider'` key. 1566 * @param {(Object|function())} provider If the provider is: 1567 * 1568 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using 1569 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. 1570 * - `Constructor`: a new instance of the provider will be created using 1571 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. 1572 * 1573 * @returns {Object} registered provider instance 1574 1575 * @example 1576 * 1577 * The following example shows how to create a simple event tracking service and register it using 1578 * {@link auto.$provide#provider $provide.provider()}. 1579 * 1580 * ```js 1581 * // Define the eventTracker provider 1582 * function EventTrackerProvider() { 1583 * var trackingUrl = '/track'; 1584 * 1585 * // A provider method for configuring where the tracked events should been saved 1586 * this.setTrackingUrl = function(url) { 1587 * trackingUrl = url; 1588 * }; 1589 * 1590 * // The service factory function 1591 * this.$get = ['$http', function($http) { 1592 * var trackedEvents = {}; 1593 * return { 1594 * // Call this to track an event 1595 * event: function(event) { 1596 * var count = trackedEvents[event] || 0; 1597 * count += 1; 1598 * trackedEvents[event] = count; 1599 * return count; 1600 * }, 1601 * // Call this to save the tracked events to the trackingUrl 1602 * save: function() { 1603 * $http.post(trackingUrl, trackedEvents); 1604 * } 1605 * }; 1606 * }]; 1607 * } 1608 * 1609 * describe('eventTracker', function() { 1610 * var postSpy; 1611 * 1612 * beforeEach(module(function($provide) { 1613 * // Register the eventTracker provider 1614 * $provide.provider('eventTracker', EventTrackerProvider); 1615 * })); 1616 * 1617 * beforeEach(module(function(eventTrackerProvider) { 1618 * // Configure eventTracker provider 1619 * eventTrackerProvider.setTrackingUrl('/custom-track'); 1620 * })); 1621 * 1622 * it('tracks events', inject(function(eventTracker) { 1623 * expect(eventTracker.event('login')).toEqual(1); 1624 * expect(eventTracker.event('login')).toEqual(2); 1625 * })); 1626 * 1627 * it('saves to the tracking url', inject(function(eventTracker, $http) { 1628 * postSpy = spyOn($http, 'post'); 1629 * eventTracker.event('login'); 1630 * eventTracker.save(); 1631 * expect(postSpy).toHaveBeenCalled(); 1632 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); 1633 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); 1634 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); 1635 * })); 1636 * }); 1637 * ``` 1638 */ 1639 1640 /** 1641 * @ngdoc method 1642 * @name $provide#factory 1643 * @description 1644 * 1645 * Register a **service factory**, which will be called to return the service instance. 1646 * This is short for registering a service where its provider consists of only a `$get` property, 1647 * which is the given service factory function. 1648 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to 1649 * configure your service in a provider. 1650 * 1651 * @param {string} name The name of the instance. 1652 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand 1653 * for `$provide.provider(name, {$get: $getFn})`. 1654 * @returns {Object} registered provider instance 1655 * 1656 * @example 1657 * Here is an example of registering a service 1658 * ```js 1659 * $provide.factory('ping', ['$http', function($http) { 1660 * return function ping() { 1661 * return $http.send('/ping'); 1662 * }; 1663 * }]); 1664 * ``` 1665 * You would then inject and use this service like this: 1666 * ```js 1667 * someModule.controller('Ctrl', ['ping', function(ping) { 1668 * ping(); 1669 * }]); 1670 * ``` 1671 */ 1672 1673 1674 /** 1675 * @ngdoc method 1676 * @name $provide#service 1677 * @description 1678 * 1679 * Register a **service constructor**, which will be invoked with `new` to create the service 1680 * instance. 1681 * This is short for registering a service where its provider's `$get` property is the service 1682 * constructor function that will be used to instantiate the service instance. 1683 * 1684 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service 1685 * as a type/class. 1686 * 1687 * @param {string} name The name of the instance. 1688 * @param {Function} constructor A class (constructor function) that will be instantiated. 1689 * @returns {Object} registered provider instance 1690 * 1691 * @example 1692 * Here is an example of registering a service using 1693 * {@link auto.$provide#service $provide.service(class)}. 1694 * ```js 1695 * var Ping = function($http) { 1696 * this.$http = $http; 1697 * }; 1698 * 1699 * Ping.$inject = ['$http']; 1700 * 1701 * Ping.prototype.send = function() { 1702 * return this.$http.get('/ping'); 1703 * }; 1704 * $provide.service('ping', Ping); 1705 * ``` 1706 * You would then inject and use this service like this: 1707 * ```js 1708 * someModule.controller('Ctrl', ['ping', function(ping) { 1709 * ping.send(); 1710 * }]); 1711 * ``` 1712 */ 1713 1714 1715 /** 1716 * @ngdoc method 1717 * @name $provide#value 1718 * @description 1719 * 1720 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a 1721 * number, an array, an object or a function. This is short for registering a service where its 1722 * provider's `$get` property is a factory function that takes no arguments and returns the **value 1723 * service**. 1724 * 1725 * Value services are similar to constant services, except that they cannot be injected into a 1726 * module configuration function (see {@link angular.Module#config}) but they can be overridden by 1727 * an Angular 1728 * {@link auto.$provide#decorator decorator}. 1729 * 1730 * @param {string} name The name of the instance. 1731 * @param {*} value The value. 1732 * @returns {Object} registered provider instance 1733 * 1734 * @example 1735 * Here are some examples of creating value services. 1736 * ```js 1737 * $provide.value('ADMIN_USER', 'admin'); 1738 * 1739 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); 1740 * 1741 * $provide.value('halfOf', function(value) { 1742 * return value / 2; 1743 * }); 1744 * ``` 1745 */ 1746 1747 1748 /** 1749 * @ngdoc method 1750 * @name $provide#constant 1751 * @description 1752 * 1753 * Register a **constant service**, such as a string, a number, an array, an object or a function, 1754 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be 1755 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot 1756 * be overridden by an Angular {@link auto.$provide#decorator decorator}. 1757 * 1758 * @param {string} name The name of the constant. 1759 * @param {*} value The constant value. 1760 * @returns {Object} registered instance 1761 * 1762 * @example 1763 * Here a some examples of creating constants: 1764 * ```js 1765 * $provide.constant('SHARD_HEIGHT', 306); 1766 * 1767 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); 1768 * 1769 * $provide.constant('double', function(value) { 1770 * return value * 2; 1771 * }); 1772 * ``` 1773 */ 1774 1775 1776 /** 1777 * @ngdoc method 1778 * @name $provide#decorator 1779 * @description 1780 * 1781 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator 1782 * intercepts the creation of a service, allowing it to override or modify the behaviour of the 1783 * service. The object returned by the decorator may be the original service, or a new service 1784 * object which replaces or wraps and delegates to the original service. 1785 * 1786 * @param {string} name The name of the service to decorate. 1787 * @param {function()} decorator This function will be invoked when the service needs to be 1788 * instantiated and should return the decorated service instance. The function is called using 1789 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. 1790 * Local injection arguments: 1791 * 1792 * * `$delegate` - The original service instance, which can be monkey patched, configured, 1793 * decorated or delegated to. 1794 * 1795 * @example 1796 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting 1797 * calls to {@link ng.$log#error $log.warn()}. 1798 * ```js 1799 * $provide.decorator('$log', ['$delegate', function($delegate) { 1800 * $delegate.warn = $delegate.error; 1801 * return $delegate; 1802 * }]); 1803 * ``` 1804 */ 1805 1806 1807 function createInjector(modulesToLoad, strictDi) { 1808 strictDi = (strictDi === true); 1809 var INSTANTIATING = {}, 1810 providerSuffix = 'Provider', 1811 path = [], 1812 loadedModules = new HashMap([], true), 1813 providerCache = { 1814 $provide: { 1815 provider: supportObject(provider), 1816 factory: supportObject(factory), 1817 service: supportObject(service), 1818 value: supportObject(value), 1819 constant: supportObject(constant), 1820 decorator: decorator 1821 } 1822 }, 1823 providerInjector = (providerCache.$injector = 1824 createInternalInjector(providerCache, function() { 1825 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); 1826 }, strictDi)), 1827 instanceCache = {}, 1828 instanceInjector = (instanceCache.$injector = 1829 createInternalInjector(instanceCache, function(servicename) { 1830 var provider = providerInjector.get(servicename + providerSuffix); 1831 return instanceInjector.invoke(provider.$get, provider, undefined, servicename); 1832 }, strictDi)); 1833 1834 1835 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); 1836 1837 return instanceInjector; 1838 1839 //////////////////////////////////// 1840 // $provider 1841 //////////////////////////////////// 1842 1843 function supportObject(delegate) { 1844 return function(key, value) { 1845 if (isObject(key)) { 1846 forEach(key, reverseParams(delegate)); 1847 } else { 1848 return delegate(key, value); 1849 } 1850 }; 1851 } 1852 1853 function provider(name, provider_) { 1854 assertNotHasOwnProperty(name, 'service'); 1855 if (isFunction(provider_) || isArray(provider_)) { 1856 provider_ = providerInjector.instantiate(provider_); 1857 } 1858 if (!provider_.$get) { 1859 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); 1860 } 1861 return providerCache[name + providerSuffix] = provider_; 1862 } 1863 1864 function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } 1865 1866 function service(name, constructor) { 1867 return factory(name, ['$injector', function($injector) { 1868 return $injector.instantiate(constructor); 1869 }]); 1870 } 1871 1872 function value(name, val) { return factory(name, valueFn(val)); } 1873 1874 function constant(name, value) { 1875 assertNotHasOwnProperty(name, 'constant'); 1876 providerCache[name] = value; 1877 instanceCache[name] = value; 1878 } 1879 1880 function decorator(serviceName, decorFn) { 1881 var origProvider = providerInjector.get(serviceName + providerSuffix), 1882 orig$get = origProvider.$get; 1883 1884 origProvider.$get = function() { 1885 var origInstance = instanceInjector.invoke(orig$get, origProvider); 1886 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); 1887 }; 1888 } 1889 1890 //////////////////////////////////// 1891 // Module Loading 1892 //////////////////////////////////// 1893 function loadModules(modulesToLoad){ 1894 var runBlocks = [], moduleFn, invokeQueue; 1895 forEach(modulesToLoad, function(module) { 1896 if (loadedModules.get(module)) return; 1897 loadedModules.put(module, true); 1898 1899 function runInvokeQueue(queue) { 1900 var i, ii; 1901 for(i = 0, ii = queue.length; i < ii; i++) { 1902 var invokeArgs = queue[i], 1903 provider = providerInjector.get(invokeArgs[0]); 1904 1905 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); 1906 } 1907 } 1908 1909 try { 1910 if (isString(module)) { 1911 moduleFn = angularModule(module); 1912 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); 1913 runInvokeQueue(moduleFn._invokeQueue); 1914 runInvokeQueue(moduleFn._configBlocks); 1915 } else if (isFunction(module)) { 1916 runBlocks.push(providerInjector.invoke(module)); 1917 } else if (isArray(module)) { 1918 runBlocks.push(providerInjector.invoke(module)); 1919 } else { 1920 assertArgFn(module, 'module'); 1921 } 1922 } catch (e) { 1923 if (isArray(module)) { 1924 module = module[module.length - 1]; 1925 } 1926 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { 1927 // Safari & FF's stack traces don't contain error.message content 1928 // unlike those of Chrome and IE 1929 // So if stack doesn't contain message, we create a new string that contains both. 1930 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. 1931 /* jshint -W022 */ 1932 e = e.message + '\n' + e.stack; 1933 } 1934 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", 1935 module, e.stack || e.message || e); 1936 } 1937 }); 1938 return runBlocks; 1939 } 1940 1941 //////////////////////////////////// 1942 // internal Injector 1943 //////////////////////////////////// 1944 1945 function createInternalInjector(cache, factory) { 1946 1947 function getService(serviceName) { 1948 if (cache.hasOwnProperty(serviceName)) { 1949 if (cache[serviceName] === INSTANTIATING) { 1950 throw $injectorMinErr('cdep', 'Circular dependency found: {0}', 1951 serviceName + ' <- ' + path.join(' <- ')); 1952 } 1953 return cache[serviceName]; 1954 } else { 1955 try { 1956 path.unshift(serviceName); 1957 cache[serviceName] = INSTANTIATING; 1958 return cache[serviceName] = factory(serviceName); 1959 } catch (err) { 1960 if (cache[serviceName] === INSTANTIATING) { 1961 delete cache[serviceName]; 1962 } 1963 throw err; 1964 } finally { 1965 path.shift(); 1966 } 1967 } 1968 } 1969 1970 function invoke(fn, self, locals, serviceName){ 1971 if (typeof locals === 'string') { 1972 serviceName = locals; 1973 locals = null; 1974 } 1975 1976 var args = [], 1977 $inject = annotate(fn, strictDi, serviceName), 1978 length, i, 1979 key; 1980 1981 for(i = 0, length = $inject.length; i < length; i++) { 1982 key = $inject[i]; 1983 if (typeof key !== 'string') { 1984 throw $injectorMinErr('itkn', 1985 'Incorrect injection token! Expected service name as string, got {0}', key); 1986 } 1987 args.push( 1988 locals && locals.hasOwnProperty(key) 1989 ? locals[key] 1990 : getService(key) 1991 ); 1992 } 1993 if (isArray(fn)) { 1994 fn = fn[length]; 1995 } 1996 1997 // http://jsperf.com/angularjs-invoke-apply-vs-switch 1998 // #5388 1999 return fn.apply(self, args); 2000 } 2001 2002 function instantiate(Type, locals, serviceName) { 2003 var Constructor = function() {}, 2004 instance, returnedValue; 2005 2006 // Check if Type is annotated and use just the given function at n-1 as parameter 2007 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); 2008 Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; 2009 instance = new Constructor(); 2010 returnedValue = invoke(Type, instance, locals, serviceName); 2011 2012 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; 2013 } 2014 2015 return { 2016 invoke: invoke, 2017 instantiate: instantiate, 2018 get: getService, 2019 annotate: annotate, 2020 has: function(name) { 2021 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); 2022 } 2023 }; 2024 } 2025 } 2026 2027 createInjector.$$annotate = annotate; 2028 2029 /** 2030 * @ngdoc service 2031 * @name $anchorScroll 2032 * @kind function 2033 * @requires $window 2034 * @requires $location 2035 * @requires $rootScope 2036 * 2037 * @description 2038 * When called, it checks current value of `$location.hash()` and scrolls to the related element, 2039 * according to rules specified in 2040 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). 2041 * 2042 * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. 2043 * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. 2044 * 2045 * @example 2046 <example module="anchorScrollExample"> 2047 <file name="index.html"> 2048 <div id="scrollArea" ng-controller="ScrollController"> 2049 <a ng-click="gotoBottom()">Go to bottom</a> 2050 <a id="bottom"></a> You're at the bottom! 2051 </div> 2052 </file> 2053 <file name="script.js"> 2054 angular.module('anchorScrollExample', []) 2055 .controller('ScrollController', ['$scope', '$location', '$anchorScroll', 2056 function ($scope, $location, $anchorScroll) { 2057 $scope.gotoBottom = function() { 2058 // set the location.hash to the id of 2059 // the element you wish to scroll to. 2060 $location.hash('bottom'); 2061 2062 // call $anchorScroll() 2063 $anchorScroll(); 2064 }; 2065 }]); 2066 </file> 2067 <file name="style.css"> 2068 #scrollArea { 2069 height: 350px; 2070 overflow: auto; 2071 } 2072 2073 #bottom { 2074 display: block; 2075 margin-top: 2000px; 2076 } 2077 </file> 2078 </example> 2079 */ 2080 function $AnchorScrollProvider() { 2081 2082 var autoScrollingEnabled = true; 2083 2084 this.disableAutoScrolling = function() { 2085 autoScrollingEnabled = false; 2086 }; 2087 2088 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { 2089 var document = $window.document; 2090 2091 // helper function to get first anchor from a NodeList 2092 // can't use filter.filter, as it accepts only instances of Array 2093 // and IE can't convert NodeList to an array using [].slice 2094 // TODO(vojta): use filter if we change it to accept lists as well 2095 function getFirstAnchor(list) { 2096 var result = null; 2097 forEach(list, function(element) { 2098 if (!result && nodeName_(element) === 'a') result = element; 2099 }); 2100 return result; 2101 } 2102 2103 function scroll() { 2104 var hash = $location.hash(), elm; 2105 2106 // empty hash, scroll to the top of the page 2107 if (!hash) $window.scrollTo(0, 0); 2108 2109 // element with given id 2110 else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); 2111 2112 // first anchor with given name :-D 2113 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); 2114 2115 // no element and hash == 'top', scroll to the top of the page 2116 else if (hash === 'top') $window.scrollTo(0, 0); 2117 } 2118 2119 // does not scroll when user clicks on anchor link that is currently on 2120 // (no url change, no $location.hash() change), browser native does scroll 2121 if (autoScrollingEnabled) { 2122 $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, 2123 function autoScrollWatchAction() { 2124 $rootScope.$evalAsync(scroll); 2125 }); 2126 } 2127 2128 return scroll; 2129 }]; 2130 } 2131 2132 var $animateMinErr = minErr('$animate'); 2133 2134 /** 2135 * @ngdoc provider 2136 * @name $animateProvider 2137 * 2138 * @description 2139 * Default implementation of $animate that doesn't perform any animations, instead just 2140 * synchronously performs DOM 2141 * updates and calls done() callbacks. 2142 * 2143 * In order to enable animations the ngAnimate module has to be loaded. 2144 * 2145 * To see the functional implementation check out src/ngAnimate/animate.js 2146 */ 2147 var $AnimateProvider = ['$provide', function($provide) { 2148 2149 2150 this.$$selectors = {}; 2151 2152 2153 /** 2154 * @ngdoc method 2155 * @name $animateProvider#register 2156 * 2157 * @description 2158 * Registers a new injectable animation factory function. The factory function produces the 2159 * animation object which contains callback functions for each event that is expected to be 2160 * animated. 2161 * 2162 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` 2163 * must be called once the element animation is complete. If a function is returned then the 2164 * animation service will use this function to cancel the animation whenever a cancel event is 2165 * triggered. 2166 * 2167 * 2168 * ```js 2169 * return { 2170 * eventFn : function(element, done) { 2171 * //code to run the animation 2172 * //once complete, then run done() 2173 * return function cancellationFunction() { 2174 * //code to cancel the animation 2175 * } 2176 * } 2177 * } 2178 * ``` 2179 * 2180 * @param {string} name The name of the animation. 2181 * @param {Function} factory The factory function that will be executed to return the animation 2182 * object. 2183 */ 2184 this.register = function(name, factory) { 2185 var key = name + '-animation'; 2186 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', 2187 "Expecting class selector starting with '.' got '{0}'.", name); 2188 this.$$selectors[name.substr(1)] = key; 2189 $provide.factory(key, factory); 2190 }; 2191 2192 /** 2193 * @ngdoc method 2194 * @name $animateProvider#classNameFilter 2195 * 2196 * @description 2197 * Sets and/or returns the CSS class regular expression that is checked when performing 2198 * an animation. Upon bootstrap the classNameFilter value is not set at all and will 2199 * therefore enable $animate to attempt to perform an animation on any element. 2200 * When setting the classNameFilter value, animations will only be performed on elements 2201 * that successfully match the filter expression. This in turn can boost performance 2202 * for low-powered devices as well as applications containing a lot of structural operations. 2203 * @param {RegExp=} expression The className expression which will be checked against all animations 2204 * @return {RegExp} The current CSS className expression value. If null then there is no expression value 2205 */ 2206 this.classNameFilter = function(expression) { 2207 if(arguments.length === 1) { 2208 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; 2209 } 2210 return this.$$classNameFilter; 2211 }; 2212 2213 this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) { 2214 2215 function async(fn) { 2216 fn && $$asyncCallback(fn); 2217 } 2218 2219 /** 2220 * 2221 * @ngdoc service 2222 * @name $animate 2223 * @description The $animate service provides rudimentary DOM manipulation functions to 2224 * insert, remove and move elements within the DOM, as well as adding and removing classes. 2225 * This service is the core service used by the ngAnimate $animator service which provides 2226 * high-level animation hooks for CSS and JavaScript. 2227 * 2228 * $animate is available in the AngularJS core, however, the ngAnimate module must be included 2229 * to enable full out animation support. Otherwise, $animate will only perform simple DOM 2230 * manipulation operations. 2231 * 2232 * To learn more about enabling animation support, click here to visit the {@link ngAnimate 2233 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service 2234 * page}. 2235 */ 2236 return { 2237 2238 /** 2239 * 2240 * @ngdoc method 2241 * @name $animate#enter 2242 * @kind function 2243 * @description Inserts the element into the DOM either after the `after` element or 2244 * as the first child within the `parent` element. Once complete, the done() callback 2245 * will be fired (if provided). 2246 * @param {DOMElement} element the element which will be inserted into the DOM 2247 * @param {DOMElement} parent the parent element which will append the element as 2248 * a child (if the after element is not present) 2249 * @param {DOMElement} after the sibling element which will append the element 2250 * after itself 2251 * @param {Function=} done callback function that will be called after the element has been 2252 * inserted into the DOM 2253 */ 2254 enter : function(element, parent, after, done) { 2255 after 2256 ? after.after(element) 2257 : parent.prepend(element); 2258 async(done); 2259 return noop; 2260 }, 2261 2262 /** 2263 * 2264 * @ngdoc method 2265 * @name $animate#leave 2266 * @kind function 2267 * @description Removes the element from the DOM. Once complete, the done() callback will be 2268 * fired (if provided). 2269 * @param {DOMElement} element the element which will be removed from the DOM 2270 * @param {Function=} done callback function that will be called after the element has been 2271 * removed from the DOM 2272 */ 2273 leave : function(element, done) { 2274 element.remove(); 2275 async(done); 2276 return noop; 2277 }, 2278 2279 /** 2280 * 2281 * @ngdoc method 2282 * @name $animate#move 2283 * @kind function 2284 * @description Moves the position of the provided element within the DOM to be placed 2285 * either after the `after` element or inside of the `parent` element. Once complete, the 2286 * done() callback will be fired (if provided). 2287 * 2288 * @param {DOMElement} element the element which will be moved around within the 2289 * DOM 2290 * @param {DOMElement} parent the parent element where the element will be 2291 * inserted into (if the after element is not present) 2292 * @param {DOMElement} after the sibling element where the element will be 2293 * positioned next to 2294 * @param {Function=} done the callback function (if provided) that will be fired after the 2295 * element has been moved to its new position 2296 */ 2297 move : function(element, parent, after, done) { 2298 // Do not remove element before insert. Removing will cause data associated with the 2299 // element to be dropped. Insert will implicitly do the remove. 2300 return this.enter(element, parent, after, done); 2301 }, 2302 2303 /** 2304 * 2305 * @ngdoc method 2306 * @name $animate#addClass 2307 * @kind function 2308 * @description Adds the provided className CSS class value to the provided element. Once 2309 * complete, the done() callback will be fired (if provided). 2310 * @param {DOMElement} element the element which will have the className value 2311 * added to it 2312 * @param {string} className the CSS class which will be added to the element 2313 * @param {Function=} done the callback function (if provided) that will be fired after the 2314 * className value has been added to the element 2315 */ 2316 addClass : function(element, className, done) { 2317 className = !isString(className) 2318 ? (isArray(className) ? className.join(' ') : '') 2319 : className; 2320 forEach(element, function (element) { 2321 jqLiteAddClass(element, className); 2322 }); 2323 async(done); 2324 return noop; 2325 }, 2326 2327 /** 2328 * 2329 * @ngdoc method 2330 * @name $animate#removeClass 2331 * @kind function 2332 * @description Removes the provided className CSS class value from the provided element. 2333 * Once complete, the done() callback will be fired (if provided). 2334 * @param {DOMElement} element the element which will have the className value 2335 * removed from it 2336 * @param {string} className the CSS class which will be removed from the element 2337 * @param {Function=} done the callback function (if provided) that will be fired after the 2338 * className value has been removed from the element 2339 */ 2340 removeClass : function(element, className, done) { 2341 className = isString(className) ? 2342 className : 2343 isArray(className) ? className.join(' ') : ''; 2344 forEach(element, function (element) { 2345 jqLiteRemoveClass(element, className); 2346 }); 2347 async(done); 2348 return noop; 2349 }, 2350 2351 /** 2352 * 2353 * @ngdoc method 2354 * @name $animate#setClass 2355 * @kind function 2356 * @description Adds and/or removes the given CSS classes to and from the element. 2357 * Once complete, the done() callback will be fired (if provided). 2358 * @param {DOMElement} element the element which will have its CSS classes changed 2359 * removed from it 2360 * @param {string} add the CSS classes which will be added to the element 2361 * @param {string} remove the CSS class which will be removed from the element 2362 * @param {Function=} done the callback function (if provided) that will be fired after the 2363 * CSS classes have been set on the element 2364 */ 2365 setClass : function(element, add, remove, done) { 2366 forEach(element, function (element) { 2367 jqLiteAddClass(element, add); 2368 jqLiteRemoveClass(element, remove); 2369 }); 2370 async(done); 2371 return noop; 2372 }, 2373 2374 enabled : noop 2375 }; 2376 }]; 2377 }]; 2378 2379 function $$AsyncCallbackProvider(){ 2380 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { 2381 return $$rAF.supported 2382 ? function(fn) { return $$rAF(fn); } 2383 : function(fn) { 2384 return $timeout(fn, 0, false); 2385 }; 2386 }]; 2387 } 2388 2389 /** 2390 * ! This is a private undocumented service ! 2391 * 2392 * @name $browser 2393 * @requires $log 2394 * @description 2395 * This object has two goals: 2396 * 2397 * - hide all the global state in the browser caused by the window object 2398 * - abstract away all the browser specific features and inconsistencies 2399 * 2400 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` 2401 * service, which can be used for convenient testing of the application without the interaction with 2402 * the real browser apis. 2403 */ 2404 /** 2405 * @param {object} window The global window object. 2406 * @param {object} document jQuery wrapped document. 2407 * @param {function()} XHR XMLHttpRequest constructor. 2408 * @param {object} $log console.log or an object with the same interface. 2409 * @param {object} $sniffer $sniffer service 2410 */ 2411 function Browser(window, document, $log, $sniffer) { 2412 var self = this, 2413 rawDocument = document[0], 2414 location = window.location, 2415 history = window.history, 2416 setTimeout = window.setTimeout, 2417 clearTimeout = window.clearTimeout, 2418 pendingDeferIds = {}; 2419 2420 self.isMock = false; 2421 2422 var outstandingRequestCount = 0; 2423 var outstandingRequestCallbacks = []; 2424 2425 // TODO(vojta): remove this temporary api 2426 self.$$completeOutstandingRequest = completeOutstandingRequest; 2427 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; 2428 2429 /** 2430 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` 2431 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. 2432 */ 2433 function completeOutstandingRequest(fn) { 2434 try { 2435 fn.apply(null, sliceArgs(arguments, 1)); 2436 } finally { 2437 outstandingRequestCount--; 2438 if (outstandingRequestCount === 0) { 2439 while(outstandingRequestCallbacks.length) { 2440 try { 2441 outstandingRequestCallbacks.pop()(); 2442 } catch (e) { 2443 $log.error(e); 2444 } 2445 } 2446 } 2447 } 2448 } 2449 2450 /** 2451 * @private 2452 * Note: this method is used only by scenario runner 2453 * TODO(vojta): prefix this method with $$ ? 2454 * @param {function()} callback Function that will be called when no outstanding request 2455 */ 2456 self.notifyWhenNoOutstandingRequests = function(callback) { 2457 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire 2458 // at some deterministic time in respect to the test runner's actions. Leaving things up to the 2459 // regular poller would result in flaky tests. 2460 forEach(pollFns, function(pollFn){ pollFn(); }); 2461 2462 if (outstandingRequestCount === 0) { 2463 callback(); 2464 } else { 2465 outstandingRequestCallbacks.push(callback); 2466 } 2467 };